Professional Documents
Culture Documents
Bây giờ, dãy số đã được sắp xếp, Nhưng thuật toán của chúng ta
không nhận ra điều đó ngay được. Thuật toán sẽ cần thêm một lần
lặp nữa để kết luận dãy đã sắp xếp khi và khi khi nó đi từ đầu tới
cuối mà không có bất kỳ lần đổi chỗ nào được thực hiện.
- Lần lặp thứ 3:
1, 2, 4, 5, 8 1, 2, 4, 5, 8
1, 2, 4, 5, 8 1, 2, 4, 5, 8
1, 2, 4, 5, 8 1, 2, 4, 5, 8
1, 2, 4, 5, 8 1, 2, 4, 5, 8
b) Mã giả:
- Input: Một mảng A[0..n-1] gồm các phần tử có thể sắp xếp
theo thứ tự.
- Output: Mảng A[0..n-1] được sắp xếp theo thứ tự tăng dần.
for i ← 0 to n − 2 do
for j ← 0 to n − 2 − i do
if A[j] > A[j + 1], then swap A[j] and A[j + 1].
end for
end for
Độ phức tạp thời gian :
Thấy ngay số phép so sánh là luôn không đổi, tức không
phụ thuộc vào tình trạng ban đầu của dãy. Với i bất kỳ, ta luôn
phải so sánh V[j] với V[j-1], mà j chạy từ n đến i + 1, tức ta
tốn n – i phép so sánh. Thêm nữa, i chạy từ 1 đến n – 1. Vậy ta
tính được số phép so sánh tổng cộng: ∑(n-i) với i chạy từ 1
đến n-1 = (n-1) + (n-2) + … + 1 = n(n-1)/2.
Số phép hoán vị (tương đương 3 phép gán) lại phụ thuộc
vào tình trạng ban đầu của dãy. Cụ thể như sau:
Trường hợp tốt nhất: Dãy ban đầu đã có thứ tự. Ta
thấy ngay ta không tốn một phép hoán vị nào.
Trường hợp xấu nhất: Dãy ban đầu có thứ tự ngược.
Xét i bất kỳ, ta thấy rằng mỗi lần so sánh a[j] với a[j-
1], ta đều phải thực hiện phép hoán vị. Điều này có
nghĩa là số phép hoán vị bằng n(n-1)/2.
Tổng kết lại, ta luôn có độ phức tạp cảu thuật toán
Selection Sort là O(n2) trong mọi trường hợp.
2. Sequential Search and Brute-Force String Matching:
2.1. Sequential Search:
a) Ý tưởng thuật toán:
Là phương pháp tìm kiếm một phần tử cho trước trong một
danh sách bằng cách duyệt lần lượt từng phần từ của danh sách đó
đến khi nào tìm được giá trị mong muốn hay đã duyệt hết qua hết
danh sách.
Sau đây là ý tưởng triển khai thuật toán:
Bắt đầu từ bản ghi đầu tiên của mảng, duyệt từ đầu mảng đến
cuối mảng với x.
Nếu phần tử đang duyệt bằng x thì trả về vị trí.
Nếu không tìm thấy bất cứ phần từ nào khi đã duyệt hết thì trả
về -1.
b) Mã giả:
- Input: Một mảng A gồm n phần tử và khóa tìm kiếm K.
- Output: Chỉ số của phần tử đầu tiên trong mảng A[0..n-1] có giá
trị bằng K hoặc -1 nếu không tìm thấy phần tử đó.
A[n] ← K
i←0
While A[i] ≠ K do
i=i+1
EndWhile
If i < n, then "Found" at i.
Else, "not found"
End
Độ phức tạp thời gian :
Trong trường hợp tốt nhất độ phức tạp của thuật toán này
là O(1), trường hơp xấu nhất là O(n), trung bình cũng là O(n).
38 | 27 | 43 | 3 9 | 82 | 10
38 | 27 43 | 3 9 | 82 10
38 27 43 3 9 82 10
27 | 38 3 | 43 9 | 82 10
3 | 27 | 38 | 43 9 | 10 | 82
3 | 9 | 10 | 27 | 38 | 43 | 82
b) Mã giả:
- Input: Mảng A[i],…,A[j] với i ≤ j.
- Output: Mảng A[i],…,A[j] đã được sắp xếp.
MergeSort(A, i, j) (* Assumption: i ≤ j. *)
1. If i = j, then return Endif
2. Let k = ⌊i+j−1 2 ⌋.
3. MergeSort(A, i, k)
4. MergeSort(A, k + 1, j)
5. Merge(A, i, k, j)
End
Merge (A, i, k, j ) (* Assumption: i ≤ k < j; A[i : k] and A[k + 1 :
j] are sorted. *)
1. p1 = i; p2 = k + 1; p3 = i
2. While p 1 ≤ k and p 2 ≤ j { 2.1 If A[p1] ≤ A[p2], then B[p3] =
A[p1], p1 = p1 + 1 Else B[p3] = A[p2], p2 = p2 + 1; Endif 2.2 p3 =
p3 + 1; }
3. (* Sao chép các phần còn lại vào B *) While p 1 ≤ k { B[p3] =
A[p1]; p1 = p1 + 1; p3 = p3 + 1; } While p 2 ≤ j { B[p3] = A[p2]; p2
= p2 + 1; p3 = p3 + 1; }
4. For r = i to j { A[r] = B[r] }
End
Độ phức tạp thời gian : Trong mọi trường hợp đều là O(nlogn)
2. Quicksort:
a) Ý tưởng thuật toán:
Là một thuật toán sắp xếp hiệu quả dựa trên việc phân chia
mảng dữ liệu thành các nhóm phần tử nhỏ hơn. Giải thuật sắp xếp
nhanh chia mảng thành hai phần bằng cách so sánh từng phần tử của
mảng với một phần tử được gọi là phần tử chốt. Một mảng bao gồm
các phần tử nhỏ hơn hoặc bằng phần tử chốt và một mảng gồm các
phần tử lớn hơn phần tử chốt:
1. Chọn phần tử chốt.
2. Khai báo 2 biến con trỏ để trỏ để duyệt 2 phía của phần tử
chốt.
3. Biến bên trái trỏ đến từng phần tử mảng con bên trái của phần
tử chốt.
4. Biến bên phải trỏ đến từng phần tử mảng con bên phải của
phần tử chốt.
5. Khi biến bên trái nhỏ hơn phần tử chốt thì di chuyển sang
phải.
6. Khi biến bên phải nhỏ hơn phần tử chốt thì di chuyển sang
trái.
7. Nếu không xảy ra trưởng hợp 5 và 6 thì tráo đổi giá trị 2 biến
trái và phải.
8. Nếu trái lơn hơn phải thì đây là giá trị chốt mới.
Ví dụ minh họa :
Cho mảng [5, 7, 2, 1, 9, 4].
Chọn pivot = 5.
5, 7, 2, 1, 9, 4 (item tiếp theo: 7 > pivot; vì thế giữ nguyên vị
trí).
5, 7, 2, 1, 9, 4 (item tiếp theo: 2 < pivot; vì thể đổi chỗ với
phần tử lớn hơn đầu tiên).
5, 2, 7, 1, 9, 4 (item tiếp theo: 1 < pivot; vì thể đổi chỗ với
phần tử lớn hơn đầu tiên).
5, 2, 1, 7, 9, 4 (item tiếp theo: 9 > pivot; vì thể giữ nguyên vị
trí).
5, 2, 1, 7, 9, 4 (item tiếp theo: 4 < pivot; vì thể đổi chỗ với
phần tử lớn hơn đầu tiên).
5, 2, 1, 4, 9, 7
Cuối cùng: đổi chỗ pivot với phần tử nhỏ hơn cuối cùng.
4, 2, 1, 5, 9, 7
Lặp lại như trên ta sẽ được mảng đã sắp xếp như sau: [1, 2, 4,
5, 7, 9]
b) Mã giả:
- Input: Mảng A[i],…,A[j] với i ≤ j.
- Output: Mảng A[i],…,A[j] đã được sắp xếp.
Partition(𝐴, 𝑖, 𝑗)
1. Let 𝑝𝑖𝑣𝑜𝑡 = 𝐴[𝑖]
2. Let ℎ = 𝑖
3. for 𝑘 = 𝑖 to j do // A[i] is the pivot; 𝐴[𝑖 + 1], ..., 𝐴[ℎ] are smaller
than pivot. 𝐴[ℎ + 1], ..., 𝐴[𝑘 − 1] are pivot
4. if 𝐴[𝑘] < 𝑝𝑖𝑣𝑜𝑡 then
5. ℎ = ℎ + 1
6. 𝑠𝑤𝑎𝑝(𝐴[𝑘], 𝐴[ℎ])
7. 𝑠𝑤𝑎𝑝(𝐴[ℎ], 𝐴[𝑖]); return h End
Quicksort(𝐴, 𝑖, 𝑗)
1. if 𝑖 < 𝑗 then
2. 𝑝 = Partition(𝐴, 𝑖, 𝑗)
3. Quicksort(𝐴, 𝑖, 𝑝 − 1)
4. Quicksort(𝐴, 𝑝 + 1, 𝑗)
5. End
Độ phức tạp thời gian : Trong mọi trường hợp là O(n2)
3. Binary Search:
a) Ý tưởng thuật toán:
Ý tưởng chính của thuật toán Binary Search khi tìm kiếm với
đầu vào là một mảng đã có thứ tự:
1. Để tìm giá trị K trong mảng, chia mảng làm đôi.
2. Xét giá trị giữa của mảng, nếu không tìm được thì xét tiếp
nửa mảng trái hoặc phải.
3. Lặp đến lại bước 1 đến khi tìm được giá trị K hoặc không
thể chia đôi mảng.
Ví dụ minh họa :
Tìm vị trí của giá trị 31 trong mảng sau:
10 | 14 | 19 | 26 | 27 | 31 | 33 | 35 | 42 | 44
0 1 2 3 4 5 6 7 8 9
Đầu tiên, chúng ta chia mảng thành hai nửa theo phép toán
sau: 0 + (9 – 0)/2 = 4 (giá trị là 4.5). Do đó 4 là chỉ-mục-giữa của
mảng.
10 | 14 | 19 | 26 | 27 | 31 | 33 | 35 | 42 | 44
0 1 2 3 4 5 6 7 8 9
Bây giờ chúng ta so sánh giá trị phần tử giữa với phần tử cần
tìm. Giá trị phần tử giữa là 27 và phần tử cần tìm là 31, do đó là
không kết nối. Bởi vì giá trị cần tìm là lớn hơn nên phần tử cần tìm
sẽ nằm ở mảng con bên phải phần tử giữa.
Chúng ta thay đổi giá trị ban-đầu thành chỉ-mục-giữa + 1 và
lại tiếp tục tìm kiếm giá trị chỉ-mục-giữa.
Bây giờ chỉ mục giữa của chúng ta là (4+1) + (9 – 4)/2 = 7 (giá
trị là 7.5). Chúng ta so sánh giá trị tại chỉ mục này với giá trị cần
tìm.
10 | 14 | 19 | 26 | 27 | 31 | 33 | 35 | 42 | 44
0 1 2 3 4 5 6 7 8 9
Giá trị tại chỉ mục 7 là không kết nối, và ngoài ra giá trị cần
tìm là nhỏ hơn giá trị tại chỉ mục 7 do đó chúng ta cần tìm trong
mảng con bên trái của chỉ mục giữa này.
Tiếp tục tìm chỉ-mục-giữa lần nữa. Lần này nó có giá trị là 5.
10 | 14 | 19 | 26 | 27 | 31 | 33 | 35 | 42 | 44
0 1 2 3 4 5 6 7 8 9
So sánh giá trị tại chỉ mục 5 với giá trị cần tìm và thấy rằng nó
kết nối.
Do đó chúng ta kết luận rằng giá trị cần tìm 31 được lưu giữ
tại vị trí chỉ mục 5.
b) Mã giả:
- Input: Một mảng A[0..n-1] đã được sắp xếp và một khóa K.
- Output: Vị trí của giá trị cần tìm nếu có trong mảng hoặc “not
found” khi không tìm thấy.
ℓ ← 0, r ← n – 1
While ℓ ≤ r do
m ← ⌊ℓ+r 2 ⌋ If K = A[m], then { "found" at m-th location}
Else If K > A[m], then ℓ ← m + 1
Else K < A[m], then r ← m − 1 Endif
Endwhile
return "not found"
End
Độ phức tạp thời gian : Trong mọi trường hợp là O(log2n)