Professional Documents
Culture Documents
D. Knuth: 40% thời gian hoạt động của các máy tính là dành cho sắp xếp!
5.1.1. Bài toán sắp xếp 5.1.1. Bài toán sắp xếp 5.1.1. Bài toán sắp xếp
5.1.1. Bài toán sắp xếp 5.1.1. Bài toán sắp xếp 5.1.1. Bài toán sắp xếp
• Các loại thuật toán sắp xếp • Các đặc trưng của một thuật toán sắp xếp:
• Ứng dụng của sắp xếp:
– Sắp xếp trong (internal sort) – Tại chỗ (in place): nếu không gian nhớ phụ mà thuật toán đòi hỏi là
– Quản trị cơ sở dữ liệu (Database management); O(1), nghĩa là bị chặn bởi hằng số không phụ thuộc vào độ dài của dãy
– Khoa học và kỹ thuật (Science and engineering); • Đòi hỏi họ dữ liệu được đưa toàn bộ vào bộ nhớ trong cần sắp xếp.
của máy tính
– Các thuật toán lập lịch (Scheduling algorithms),
• ví dụ thiết kế chương trình dịch, truyền thông,... (compiler – Sắp xếp ngoài (external sort) – Ổn định (stable): Nếu các phần tử có cùng giá trị vẫn giữ nguyên thứ tự
tương đối của chúng như trước khi sắp xếp.
design, telecommunication); • Họ dữ liệu không thể cùng lúc đưa toàn bộ vào bộ nhớ
– Máy tìm kiếm web (Web search engine); trong, nhưng có thể đọc vào từng bộ phận từ bộ nhớ
– và nhiều ứng dụng khác… ngoài
Create PDF files without this message by purchasing 7novaPDF printer (http://www.novapdf.com)
NGUYỄN ĐỨC NGHĨA
Bộ môn KHMT - ĐHBKHN
NGUYỄN ĐỨC NGHĨA
Bộ môn KHMT - ĐHBKHN
8 NGUYỄN ĐỨC NGHĨA
Bộ môn KHMT - ĐHBKHN
9
5.1.1. Bài toán sắp xếp 5.1.2. Giới thiệu sơ lược về các thuật toán sắp xếp Các thuật toán sắp xếp dựa trên phép so sánh
• 2 phép toán cơ bản thường phải sử dụng: Name Average Worst Memory Stable Method
—
– Đổi chỗ (Swap): Thời gian thực hiện là O(1)
Bubble sort O(n²) O(1) Yes Exchanging
Cocktail sort — O(n²) O(1) Yes Exchanging
Simple Fancier Comparison Specialized Handling Comb sort O(n log n) O(n log n) O(1) No Exchanging
void swap( datatype &a, datatype &b){ algorithms: algorithms: lower bound: algorithms: huge data Gnome sort — O(n²) O(1) Yes Exchanging
datatype temp = a; //datatype-kiểu dữ liệu của phần tử O(n2) O(n log n) (n log n) O(n) sets Selection sort O(n²) O(n²) O(1) No Selection
• false nếu trái lại. … Heapsort O(n log n) O(n log n) O(1) No Selection
Smoothsort — O(n log n) O(1) No Selection
Quicksort O(n log n) O(n²) O(log n) No Partitioning
Introsort O(n log n) O(n log n) O(log n) No Hybrid
Patience sorting — O(n²) O(n) No Insertion
Các thuật toán sắp xếp không dựa trên phép so sánh NỘI DUNG 5.2. Ba thuật toán sắp xếp cơ bản
Name Average Worst Memory Stable n << 2k? 5.1. Bài toán sắp xếp • 5.2.1. Sắp xếp chèn (Insertion Sort)
Pigeonhole sort O(n+2k) O(n+2k) O(2k) Yes Yes
5.2. Ba thuật toán sắp xếp cơ bản • 5.2.2. Sắp xếp lựa chọn (Selection Sort)
Bucket sort O(n·k) O(n²·k) O(n·k) Yes No • 5.2.3. Sắp xếp nổi bọt (Bubble Sort)
Counting sort O(n+2k) O(n+2k) O(n+2k) Yes Yes
5.3. Sắp xếp trộn (Merge Sort)
LSD Radix sort O(n·k/s) O(n·k/s) O(n) Yes No 5.4. Sắp xếp nhanh (Quick Sort)
MSD Radix sort O(n·k/s) O(n·(k/s)·2s) O((k/s)·2s) No No 5.5. Sắp xếp vun đống (Heap Sort)
5.6. Cận dưới cho độ phức tạp tính toán của bài toán sắp xếp
Spreadsort O(n·k/log(n)) O(n·(k-log(n))0.5) O(n) No No
5.7. Các phương pháp sắp xếp đặc biệt
Các thông số trong bảng
n - số phần tử cần sắp xếp
k - kích thước mỗi phần tử
s - kích thước bộ phận được sử dụng khi cài đặt.
Nhiều thuật toán được xây dựng dựa trên giả thiết là n << 2k.
NGUYỄN ĐỨC NGHĨA 13 NGUYỄN ĐỨC NGHĨA 14 NGUYỄN ĐỨC NGHĨA 15
Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN
5.2.1. Sắp xếp chèn (Insertion Sort) Sắp xếp chèn (Insertion Sort) Sắp xếp chèn (Insertion Sort)
Phỏng theo cách làm của người Phỏng theo cách làm của người Phỏng theo cách làm của người
chơi bài khi cần "chèn" thêm một chơi bài khi cần "chèn" thêm một chơi bài khi cần "chèn" thêm một
con bài vào bộ bài đã được sắp con bài vào bộ bài đã được sắp con bài vào bộ bài đã được sắp
xếp trên tay. xếp trên tay. xếp trên tay.
Để chèn 12, ta cần tạo chỗ cho Để chèn 12, ta cần tạo chỗ cho Để chèn 12, ta cần tạo chỗ cho
nó bởi việc dịch chuyển đầu tiên nó bởi việc dịch chuyển đầu tiên nó bởi việc dịch chuyển đầu tiên
là 36 và sau đó là 24. là 36 và sau đó là 24. là 36 và sau đó là 24.
• Thuật toán:
You must design a working algorithm first. – Tại bước k = 1, 2, ..., n, đưa phần tử thứ k trong mảng đã cho vào đúng
Phỏng theo cách làm của người vị trí trong dãy gồm k phần tử đầu tiên.
chơi bài khi cần "chèn" thêm một – Kết quả là sau bước k, k phần tử đầu tiên là được sắp thứ tự.
con bài vào bộ bài đã được sắp void insertionSort(int a[], int array_size) {
xếp trên tay. int i, j, last;
Giải thích:
for (i=1; i < array_size; i++) {
Để chèn 12, ta cần tạo chỗ cho • Ở đầu lần lặp i của vòng
last = a[i];
nó bởi việc dịch chuyển đầu tiên "for" ngoài, dữ liệu từ a[0]
j = i;
là 36 và sau đó là 24. đến a[i-1] là được sắp xếp.
while ((j > 0) && (a[j-1] > last)) { • Vòng lặp "while" tìm vị trí
a[j] = a[j-1]; cho phần tử tiếp theo (last
j = j - 1; }// end while =a[i]) trong dãy gồm i phần
a[j] = last; tử đầu tiên.
} // end for
} // end of isort
NGUYỄN ĐỨC NGHĨA 19 20 NGUYỄN ĐỨC NGHĨA 21
Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN
Ví dụ Insertion sort (1) Ví dụ Insertion sort (1) Ví dụ: Insertion Sort (2)
1 2 3 8 7 9 10 12 23 18 15 16 17 14 1 2 3 7 8 9 10 12 15 16 18 17 23 14
1 2 3 7 8 9 10 12 23 18 15 16 17 14 1 2 3 7 8 9 10 12 15 16 17 18 23 14
1 2 3 7 8 9 10 12 18 23 15 16 17 14 1 2 3 7 8 9 10 12 15 16 17 18 14 23
1 2 3 7 8 9 10 12 18 15 23 16 17 14 1 2 3 7 8 9 10 12 15 16 17 14 18 23
1 2 3 7 8 9 10 12 15 18 23 16 17 14 1 2 3 7 8 9 10 12 15 16 14 17 18 23
1 2 3 7 8 9 10 12 15 18 16 23 17 14 1 2 3 7 8 9 10 12 15 14 16 17 18 23
1 2 3 7 8 9 10 12 14 15 16 17 18 23
1 2 3 7 8 9 10 12 15 16 18 23 17 14
Các đặc tính của Insertion Sort Thử nghiệm insertion sort 5.2.2. Sắp xếp chọn (Selection Sort)
• Sắp xếp chèn là tại chỗ và ổn định (In place and Stable)
#include <stdlib.h> • Thuật toán void swap(int &a,int &b)
#include <stdio.h>
– Tìm phần tử nhỏ nhất đưa vào vị trí 1 {
void insertionSort(int a[], int array_size);
• Phân tích thời gian tính của thuật toán int a[1000]; – Tìm phần tử nhỏ tiếp theo đưa vào vị trí 2
int temp = a;
a = b;
– Best Case: 0 hoán đổi, n-1 so sánh (khi dãy đầu vào là đã được sắp) int main() {
– Tìm phần tử nhỏ tiếp theo đưa vào vị trí 3
b = temp;
int i, N; }
– Worst Case: n2/2 hoán đổi và so sánh (khi dãy đầu vào có thứ tự printf("\nGive n = "); scanf("%i", &N); – …
ngược lại với thứ tự cần sắp xếp) //seed random number generator void selectionSort(int a[], int n){
– Average Case: n2/4 hoán đổi và so sánh srand(getpid());
int i, j, min, temp;
//fill array with random integers
• Thuật toán này có thời gian tính trong tình huống tốt nhất là for (i = 0; i < N; i++) for (i = 0; i < n-1; i++) {
tốt nhất a[i] = rand(); min = i;
//perform insertion sort on array
for (j = i+1; j < n; j++){
• Là thuật toán sắp xếp tốt đối với dãy đã gần được sắp xếp insertionSort(a, N);
if (a[j] < a[min]) min = j;
printf("\nOrdered sequence:\n");
– Nghĩa là mỗi phần tử đã đứng ở vị trí rất gần vị trí trong thứ tự cần for (i = 0; i < N; i++) }
sắp xếp printf("%8i", a[i]); swap(a[i], a[min]);
getch();
}
}
}
NGUYỄN ĐỨC NGHĨA NGUYỄN ĐỨC NGHĨA NGUYỄN ĐỨC NGHĨA
Create PDF files without this message by purchasing novaPDF printer (http://www.novapdf.com)
25
Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN
26
Bộ môn KHMT - ĐHBKHN
27
Selection Sort Ví dụ: Selection Sort 5.2.3. Sắp xếp nổi bọt - Bubble Sort
Nhận xét: • Đơn giản, thường được sử dụng như ví dụ minh hoạ cho các giáo
trình nhập môn lập trình.
• Best case: 0 đổi chỗ (n-1 như trong đoạn mã), n2/2 so sánh.
• Worst case: n - 1 đổi chỗ và n2/2 so sánh. • Kém hiệu quả hơn các thuật toán khác -> ngoài mục đích giảng dạy,
Sắp xếp nổi bọt rất ít khi được sử dụng.
• Average case: O(n) đổi chỗ và n2/2 so sánh.
• Ưu điểm nổi bật của sắp xếp chọn là số phép đổi chỗ là ít.
Điều này là có ý nghĩa nếu như việc đổi chỗ là tốn kém.
5.2.3. Sắp xếp nổi bọt - Bubble Sort 5.2.3. Sắp xếp nổi bọt - Bubble Sort Ví dụ: Bubble Sort
• Bắt đầu từ đầu dãy, thuật toán tiến hành so sánh mỗi phần tử với void swap(int &a,int &b)
void bubbleSort(int a[], int n){ {
phần tử đi sau nó và thực hiện đổi chỗ, nếu chúng không theo đúng 7 4 1 9 2 4 1 7 2 9 1 4 2 7 9
int i, j; int temp = a;
thứ tự. a = b;
for (i = (n-1); i >= 0; i--) { b = temp; 4 7 1 9 2 1 4 7 2 9 1 4 2 7 9
• Quá trình này sẽ được lặp lại cho đến khi gặp lần duyệt từ đầu dãy for (j = 1; j <= i; j++){ }
đến cuối dãy mà không phải thực hiện đổi chỗ (tức là tất cả các phần if (a[j-1] > a[j]) 4 1 7 9 2 1 4 7 2 9 1 2 4 7 9
tử đã đứng đúng vị trí). swap(a[j-1],a[j]); 4 1 7 9 2 1 4 2 7 9
• Cách làm này đã đẩy phần tử lớn nhất xuống cuối dãy, trong khi đó }
những phần tử có giá trị nhỏ hơn được dịch chuyển về đầu dãy. } 4 1 7 2 9
}
• Best case: 0 đổi chỗ, n2/2 so sánh. i= 4 i= 3 i= 2
• Worst case: n2/2 đổi chỗ và so sánh.
Chú ý:
• Average case: n2/4 đổi chỗ và n2/2 so sánh. – Các phần tử được đánh chỉ số bắt đầu từ 0.
– n=5
So sánh ba thuật toán cơ bản Tổng kết 3 thuật toán sắp xếp cơ bản NỘI DUNG
• Chia (Divide) 5 2 4 7 1 3 2 6
– Chia dãy gồm n phần tử cần sắp xếp ra thành 2 dãy, mỗi dãy có n/2 if p < r Kiểm tra điều kiện neo
phần tử
then q ← (p + r)/2
• Trị (Conquer) Chia (Divide)
– Sắp xếp mỗi dãy con một cách đệ qui sử dụng sắp xếp trộn MERGE-SORT(A, p, q) Trị (Conquer)
– Khi dãy chỉ còn một phần tử thì trả lại phần tử này MERGE-SORT(A, q + 1, r) Trị (Conquer)
Hoà nhập hai dòng xe theo Khoá
[độ liều lĩnh của lái xe]. • Tổ hợp (Combine) MERGE(A, p, q, r) Tổ hợp (Combine)
Lái xe liều lĩnh hơn sẽ vào trước! – Trộn (Merge) hai dãy con được sắp xếp để thu được dãy được sắp xếp
endif
gồm tất cả các phần tử của cả hai dãy con
• Lệnh gọi thực hiện thuật toán: MERGE-SORT(A, 1, n)
Trộn (Merge) - Pseudocode Thời gian tính của trộn Thời gian tính của sắp xếp trộn
MERGE-SORT Running Time
MERGE(A, p, q, r) • Khởi tạo (tạo hai mảng con tạm thời L và R): • Chia:
1. Tính n1 và n2 p q r
1 2 3 4 5 6 7 8 – (n1 + n2) = (n) – tính q như là giá trị trung bình của p và r: D(n) = (1)
2. Sao n1 phần tử đầu tiên vào L[1 . . n1] và n2
phần tử tiếp theo vào R[1 . . n2]
2 4 5 7 1 2 3 6
• Đưa các phần tử vào mảng kết quả (vòng lặp for cuối cùng): • Trị:
3. L[n1 + 1] ← ; R[n2 + 1] ←
n1 n2 – n lần lặp, mỗi lần đòi hởi thời gian hằng số (n) – giải đệ qui 2 bài toán con, mỗi bài toán kích thước n/2 2T (n/2)
4. i ← 1; j ← 1 • Tổ hợp:
• Tổng cộng thời gian của trộn là:
5. for k ← p to r do p q
– TRỘN (MERGE) trên các mảng con cỡ n phần tử đòi hỏi thời gian (n)
if L[ i ] ≤ R[ j ] – (n)
6.
L 2 4 5 7 C(n) = (n)
7. then A[k] ← L[ i ]
8. i ←i + 1
q+1 r
(1) nếu n =1
1 2 3 6
9. else A[k] ← R[ j ]
R
T(n) = 2T(n/2) + (n) nếu n > 1
10. j←j+1 – Suy ra theo định lý thợ: T(n) = (n log n)
NGUYỄN ĐỨC NGHĨA 40 NGUYỄN ĐỨC NGHĨA 41 NGUYỄN ĐỨC NGHĨA 42
Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN
Ví dụ: Sắp xếp trộn Cài đặt merge: Trộn A[first..mid] và A[mid+1.. last] Cài đặt mergesort
1 2 3 4 5 6 7 8
void merge(DataType A[], int first, int mid, int last){ void mergesort(DataType A[], int first, int last)
8 2 9 4 5 3 1 6 DataType tempA[MAX_SIZE]; // mảng phụ
{
1 2 3 4 5 6 7 8
int first1 = first; int last1 = mid;
if (first < last)
Divide 8 2 9 4 5 3 1 6 int first2 = mid + 1; int last2 = last; int index = first1;
{ // chia thành hai dãy con
for (; (first1 <= last1) && (first2 <= last2); ++index){
int mid = (first + last)/2; // chỉ số điểm giữa
1 2 3 4 5 6 7 8
if (A[first1] < A[first2]) {
Divide 8 2 9 4 5 3 1 6 tempA[index] = A[first1]; ++first1;} // sắp xếp dãy con trái A[first..mid]
1 2 3 4 5 6 7 8
else mergesort(A, first, mid);
1 phần tử 8 2 9 4 5 3 1 6 { tempA[index] = A[first2]; ++first2;} } // sắp xếp dãy con phải A[mid+1..last]
1 2 3 4 5 6 7 8
for (; first1 <= last1; ++first1, ++index) mergesort(A, mid+1, last);
tempA[index] = A[first1]; // sao nốt dãy con 1
Merge 2 8 4 9 3 5 1 6 // Trộn hai dãy con
for (; first2 <= last2; ++first2, ++index)
merge(A, first, mid, last);
1 2 3 4 5 6 7 8
tempA[index] = A[first2]; // sao nốt dãy con 2
Merge 2 4 8 9 1 3 5 6 for (index = first; index <= last; ++index)
} // end if
A[index] = tempA[index]; // sao trả mảng kết quả } // end mergesort
1 2 3 4 5 6 7 8
} // end merge
Kết quả: 1 2 3 4 5 6 8 9
Chú ý: DataType: kiểu dữ liệu phần tử mảng. Lệnh gọi thực hiện mergesort(A,0,n-1)
NGUYỄN ĐỨC NGHĨA NGUYỄN ĐỨC NGHĨA NGUYỄN ĐỨC NGHĨA
Create PDF files without this message by purchasing novaPDF printer (http://www.novapdf.com)
Bộ môn KHMT - ĐHBKHN
43
Bộ môn KHMT - ĐHBKHN
44
Bộ môn KHMT - ĐHBKHN
45
NỘI DUNG 5.4. Sắp xếp nhanh (Quick Sort) 5.4.1. Sơ đồ Quick Sort
5.1. Bài toán sắp xếp • 5.4.1. Sơ đồ tổng quát • Thuật toán sắp xếp nhanh được phát triển bởi
• 5.4.2. Phép phân đoạn Hoare năm 1960 khi ông đang làm việc cho
5.2. Ba thuật toán sắp xếp cơ bản hãng máy tính nhỏ Elliott Brothers ở Anh.
• 5.4.3. Độ phức tạp của sắp xếp nhanh
5.3. Sắp xếp trộn (Merge Sort) • Theo thống kê tính toán, Quick sort (sẽ viết tắt là
QS) là thuật toán sắp xếp nhanh nhất hiện nay.
5.4. Sắp xếp nhanh (Quick Sort)
• QS có thời gian tính trung bình là O(n log n), tuy C.A.R. Hoare
5.5. Sắp xếp vun đống (Heap Sort) nhiên thời gian tính tồi nhất của nó lại là O(n2). January 11, 1934
ACM Turing Award, 1980
5.6. Cận dưới cho độ phức tạp tính toán của bài toán sắp xếp • QS là thuật toán sắp xếp tại chỗ, nhưng nó Photo: 2006
có ảnh hưởng sâu rộng đến việc phát triển và ứng dụng của khoa • Dựa trên kỹ thuật chia để trị. 1. Neo đệ qui (Base case). Nếu dãy chỉ còn không quá 1 phần tử thì nó là
học kỹ thuật …
• Có thể được mô tả đệ qui tương tự như Merge Sort dãy được sắp và trả lại ngay dãy này mà không phải làm gì cả.
1946: The Metropolis Algorithm for Monte Carlo • Ngược lại với Merge Sort, trong QS 2. Chia (Divide):
1947: Simplex Method for Linear Programming // Tính toán khoa học
– thao tác chia là phức tạp, • Chọn 1 phần tử trong dãy và gọi nó là phần tử chốt p (pivot).
1950: Krylov Subspace Iteration Method
1951: The Decompositional Approach to Matrix Computations – nhưng thao tác tổng hợp lại đơn giản. • Chia dãy đã cho ra thành 2 dãy con:
1957: The Fortran Optimizing Compiler – Dãy con trái (L) gồm những phần tử không lớn hơn phần tử
• Điểm mấu chốt để thực hiện QS chính là thao tác chia.
1959: QR Algorithm for Computing Eigenvalues chốt,
1962: Quicksort Algorithms for Sorting • => Phụ thuộc vào thuật toán thực hiện thao tác này mà ta có các – Dãy con phải (R) gồm các phần tử không nhỏ hơn phần tử
1965: Fast Fourier Transform // Xử lý ảnh dạng QS cụ thể. chốt. Thao tác này được gọi là "Phân đoạn" (Partition).
1977: Integer Relation Detection
1987: Fast Multipole Method 3. Trị (Conquer): Lặp lại một cách đệ qui thuật toán đối với 2 dãy con L
và R.
Computing in Science & Engineering, January/February 2000
4. Tổng hợp (Combine): Dãy được sắp xếp là L p R.
Science, Vol. 287, No. 5454, p. 799, February 2000
Các bước của QuickSort Sơ đồ tổng quát của QS Sơ đồ tổng quát của QS
• Sơ đồ tổng quát của QS có thể mô tả như sau: • Khi dãy con chỉ còn một số lượng không lớn phần tử (VD: 9 phần
A 81 43 31 57 Chọn pivot
Quick-Sort(A, Left, Right) tử) -> sử dụng các thuật toán đơn giản để sắp xếp dãy này, chứ
13 75
92
26
0 không nên tiếp tục chia nhỏ.
65 1. if (Left < Right ) {
• Thuật toán trong tình huống như vậy có thể mô tả như sau:
2. Pivot = Partition(A, Left, Right);
L R Phân đoạn A
0
13 43 31
65
75
81
3. Quick-Sort(A, Left, Pivot –1);
26 57
92 Quick-Sort(A, Left, Right)
4. Quick-Sort(A, Pivot +1, Right); }
1. if (Right - Left < n0 )
QuickSort(L) và
L R QuickSort(R) 2. Insertion_sort(A, Left, Right);
0 13 26 31 43 57 65 75 81 92 • Hàm Partition(A, Left, Right) thực hiện chia A[Left..Right] thành 2
đoạn A[Left..Pivot –1 ] và A[Pivot+1..Right] sao cho: 3. else {
• Các phần tử trong A[Left..Pivot –1] ≤ A[Pivot] 4. Pivot = Partition(A, Left, Right);
A 0 13 26 31 43 57 65 75 81 92 OK! A được sắp • Các phần tử trong A[Pivot+1..Right] ≥ A[Pivot]. 5. Quick-Sort(A, Left, Pivot –1);
• Lệnh gọi thực hiện thuật toán Quick-Sort(A, 1, n) 6. Quick-Sort(A, Pivot +1, Right); }
NGUYỄN ĐỨC NGHĨA NGUYỄN ĐỨC NGHĨA NGUYỄN ĐỨC NGHĨA
Create PDF files without this message by purchasing52novaPDF printer (http://www.novapdf.com)
Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN
53
Bộ môn KHMT - ĐHBKHN
54
5.4.2. Thao tác chia Chọn phần tử chốt Chọn phần tử chốt
• Trong QS thao tác chia bao gồm 2 công việc:
– Chọn phần tử chốt p. • Việc chọn phần tử chốt có vai trò quyết định đối với hiệu quả
của thuật toán. • Người ta thường sử dụng các cách chọn phần tử chốt sau đây:
– Phân đoạn: Chia dãy đã cho ra thành 2 dãy con
• Thao tác phân đoạn có thể cài đặt (tại chỗ) với thời gian (n). • Tốt nhất nếu chọn được phần tử chốt là phần tử đứng giữa – Chọn phần tử trái nhất (đứng đầu) làm phần tử chốt.
• Hiệu quả của thuật toán phụ thuộc rất nhiều vào việc phần tử nào trong danh sách được sắp xếp (ta gọi phần tử như vậy là trung – Chọn phần tử phải nhất (đứng cuối) làm phần tử chốt.
được chọn làm phần tử chốt: vị/median). – Chọn phần tử đứng giữa danh sách làm phần tử chốt.
– Thời gian tính trong tình huống tồi nhất của QS là O(n2). • Khi đó, sau log2n lần phân đoạn ta sẽ đạt tới danh sách với – Chọn phần tử trung vị trong 3 phần tử đứng đầu, đứng giữa
• Xảy ra khi danh sách là đã được sắp xếp và phần tử chốt kích thước bằng 1. và đứng cuối làm phần tử chốt ().
được chọn là phần tử trái nhất của dãy. • Tuy nhiên, điều đó rất khó thực hiện. – Chọn ngẫu nhiên một phần tử làm phần tử chốt.
– Nếu phần tử chốt được chọn ngẫu nhiên -> QS có độ phức tạp
tính toán là O(n log n).
• Ta xây dựng hàm Partition(a, left, right) làm việc sau: Partition(a, left, right)
pivot được chọn là Vị trí: 0 1 2 3 4 5 6 7 8 9
i = left; j = right + 1; pivot = a[left];
• Input: Mảng a[left .. right]. while i < j do
phần tử đứng đầu
Khoá (Key): 9 1 11 17 13 18 4 12 14 5
• Output: Phân bố lại các phần tử của mảng đầu vào và trả lại i = i + 1; > > <
chỉ số jpivot thoả mãn: while i right and a[i] < pivot do i = i + 1; lần 1while: 9 1 5 17 13 18 4 12 14 11
j = j – 1;
– a[jpivot] chứa giá trị ban đầu của a[left], > < < <
while j left and a[j] > pivot do j = j – 1; lần 2while: 9 1 5 4 13 18 17 12 14 11
– a[i] ≤ a[jpivot], với mọi left ≤ i < pivot, swap(a[i] , a[j]); < >< <
j là chỉ số (jpivot) cần trả lại,
– a[j] ≥ a[jpivot]), với mọi pivot < j ≤ right. swap(a[i], a[j]); swap(a[j], a[left]); do đó cần đổi chỗ a[left] và a[j]
lần 3while: 9 1 5 13 4 18 17 12 14 11
return j;
2 lần đổi chỗ: 4 1 5 9 13 18 17 12 14 11
i Sau khi chọn pivot, dịch các con trỏ i và j từ đầu và cuối mảng j
và đổi chỗ cặp phần tử thoả mãn a[i] > pivot và a[j] < pivot
Ví dụ: Phân đoạn với pivot là phần tử đứng đầu Phần tử chốt là phần tử đứng giữa Phần tử chốt là phần tử đứng cuối
PartitionMid(a, left, right);
Chọn pivot: 7 2 8 3 5 9 6
pivot được chọn là
int PartitionR(int a[], int p, int r) { void quickSort(int a[], int p, int r) {
i = left; j = right; pivot = a[(left + right)/2]; int x = a[r]; if (p < r) {
phần tử đứng giữa
Phân đoạn: Con trỏ 7 2 8 3 5 9 6 repeat int j = p - 1; int q = PartitionR(a, p, r);
< > while a[i] < pivot do i = i + 1; for (int i = p; i < r; i++) { quickSort(a, p, q - 1);
2 nhỏ hơn pivot 7 2 8 3 5 9 6 while pivot < a[j] do j = j - 1; if (x >= a[i]) quickSort(a, q + 1, r);
< > if i <= j { j = j + 1; swap(a[i], a[j]); } }
đổi chỗ 6, 8 7 2 6 3 5 9 8 swap(a[i], a[j]); } }
< > i = i + 1; j = j - 1; a[r] = a[j + 1]; a[j + 1] = x;
3,5 nhỏ hơn 9 lớn hơn 7 2 6 3 5 9 8 until i > j; return (j + 1);
return j; }
Kết thúc phân đoạn 7 2 6 3 5 9 8
• Cài đặt thuật toán phân đoạn trong đó pivot được chọn là phần tử đứng
Đưa pivot vào vị trí 5 2 6 3 7 9 8 giữa (Đây cũng là cách cài đặt mà TURBO C lựa chọn).
Khi phần tử chốt được chọn là phần tử đứng đầu: Phân đoạn hoàn hảo - Perfect partition Thời gian tính trung bình của QS
Tình huống tồi nhất xảy ra khi dãy đầu vào là đã được sắp xếp QuickSort Average Case
1 2 3 4 5 6 7 8 9 10 11 Công thức đệ qui: • Giả sử rằng pivot được chọn ngẫu nhiên trong số các
phần tử của dãy đầu vào
1 2 3 4 5 6 7 8 9 10 11 T(n) = T(n/2) + T(n/2) + Θ(n) • Tất cả các tình huống sau đây là đồng khả năng:
– Pivot là phần tử nhỏ nhất trong dãy
T (n ) = 2T (n / 2) + (n ) – Pivot là phần tử nhỏ nhì trong dãy
2 3 4 5 6 7 8 9 10 11
– Pivot là phần tử nhỏ thứ ba trong dãy
T ( n ) = ( n log n ) It's Best Case!
…
3 4 5 6 7 8 9 10 11 – Pivot là phần tử lớn nhất trong dãy
Theo định lý thợ ! • Điều đó cũng là đúng khi pivot luôn được chọn là phần
T (0) = T (1) = 1 4 5 6 7 8 9 10 11 tử đầu tiên, với giả thiết là dãy đầu vào là hoàn toàn
ngẫu nhiên
T ( n ) = T (n − 1) + n
NGUYỄN ĐỨC NGHĨA 71
Create PDF files without this message by purchasing novaPDF printer (http://www.novapdf.com)
70
Bộ môn KHMT - ĐHBKHN
72
Thời gian tính trung bình của QS
QuickSort Average Case
NỘI DUNG 5.5. Sắp xếp vun đống (Heap Sort)
• Thời gian tính trung bình = 5.1. Bài toán sắp xếp 5.5.1. Cấu trúc dữ liệu đống (heap)
5.2. Ba thuật toán sắp xếp cơ bản 5.5.2. Sắp xếp vun đống
(thời gian phân đoạn kích thước i)(xác suất phân đoạn có kích thước i)
5.5.3. Hàng đợi có ưu tiên (priority queue)
• Trong tình huống ngẫu nhiên, tất cả các kích thước là đồng khả 5.3. Sắp xếp trộn (Merge Sort)
năng - xác suất chính là 1/N 5.4. Sắp xếp nhanh (Quick Sort)
T ( N ) = T (i ) + T ( N − i −1) + cN 5.5. Sắp xếp vun đống (Heap Sort)
E (T ( N )) = i =0 (1/ N ) E (T (i)) + E (T ( N − i − 1) ) + cN
N −1
5.6. Cận dưới cho độ phức tạp tính toán của bài toán sắp xếp
E (T ( N )) (2 / N ) i =0 E (T (i) ) + cN
N −1
5.7. Các phương pháp sắp xếp đặc biệt
5.5.1. The Heap Data Structure Biểu diễn đống bởi mảng Ví dụ đống
• Định nghĩa: Đống (heap) là cây nhị phân gần hoàn chỉnh có 2 • Đống có thể cất giữ trong mảng A.
1
parent(i) = i/2
tính chất: – Gốc của cây là A[1] 0
16 left-child(i) = 2i
– Tính cấu trúc: tất cả các mức đều là đầy, ngoại trừ mức cuối cùng, – Con trái của A[i] là A[2*i] right-child(i) = 2i +1
mức cuối được điền từ trái sang phải. 2 3
– Con phải của A[i] là A[2*i + 1]
– Tính có thứ tự hay tính chất đống : với mỗi nút x 1 14 10
Parent(x) ≥ x. – Cha của A[i] là A[ i/2 ]
• Cây được cài đặt bởi mảng A[i] có độ dài length[A]. Số lượng – Heapsize[A] ≤ length[A] 4 5 6 7
Hai dạng đống Bổ sung và loại bỏ nút Các phép toán đối với đống
Adding/Deleting Nodes Operations on Heaps
• Đống max - Max-heaps (Phần tử lớn nhất ở gốc), có tính chất • Nút mới được bổ sung vào mức đáy (từ trái sang phải) • Khôi phục tính chất max-heap (Vun lại đống)
max-heap: • Các nút được loại bỏ khỏi mức đáy (từ phải sang trái) – Max-Heapify
– với mọi nút i, ngoại trừ gốc:
A[parent(i)] ≥ A[i]
• Tạo max-heap từ một mảng không được sắp xếp
– Build-Max-Heap
• Đống min - Min-heaps (phần tử nhỏ nhất ở gốc), có tính chất
min-heap:
– với mọi nút i, ngoại trừ gốc:
A[parent(i)] ≤ A[i]
• Phần dưới đây ta sẽ chỉ xét đống max (max-heap). Đống min
được xét hoàn toàn tương tự.
NGUYỄN ĐỨC NGHĨA NGUYỄN ĐỨC NGHĨA NGUYỄN ĐỨC NGHĨA
Create PDF files without this message by purchasing79novaPDF printer (http://www.novapdf.com)
Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN
80
Bộ môn KHMT - ĐHBKHN
81
Khôi phục tính chất đống Ví dụ Thuật toán khôi phục tính chất đống
• Giả sử có nút i với giá trị bé hơn con của nó • Giả thiết: Max-Heapify(A, i, n)
– Giả thiết là: Cây con trái và Cây con phải của i đều là max-heaps – Cả hai cây con trái // n = heapsize[A]
A[2] A[4]
• Để loại bỏ sự vi phạm này ta tiến hành như sau: và phải của i đều là 1. l left-child(i)
– Đổi chỗ với con lớn hơn
max-heaps 2. r right-child(i)
– A[i] có thể bé hơn 3. if (l ≤ n) and (A[l] > A[i])
– Di chuyển xuống theo cây các con của nó
– Tiếp tục quá trình cho đến khi nút không còn bé hơn con A[2] vi phạm tính chất đống A[4] vi phạm 4. then largest l
5. else largest i
6. if (r ≤ n) and (A[r] > A[largest])
7. then largest r
A[4] A[9] 8. if largest != i
9. then Exchange(A[i],A[largest])
10. Max-Heapify(A,largest,n)
Tính chất đống được khôi phục
Thời gian tính của MAX-HEAPIFY Xây dựng đống (Building a Heap) Ví dụ: A: 4 1 3 2 16 9 10 14 8 7
i =1 5 i= 4 i =1 3
• Ta nhận thấy rằng: • Biến đổi mảng A[1 … n] thành max-heap (n = length[A]) 1
4 4 4
– Từ nút i phải di chuyển theo đuờng đi xuống phía dưới của cây. Độ dài • Vì các phần tử của mảng con A[(n/2+1) .. n] là các lá 2 3 2 3 2 3
của đường đi này không vượt quá độ dài đường đi từ gốc đến lá, nghĩa 1 3 1 3 1 3
là khôngvượt quá h.
• Do đó để tạo đống ta chỉ cần áp dụng MAX-HEAPIFY đối với 4 5 6 7 4 5 6 7 4 5 6 7
2 16 9 10 8 2 16 9 10 8 14 16 9 10
– Ở mỗi mức phải thực hiện 2 phép so sánh.
các phần tử từ 1 đến n/2 8 9 10 9 10 9 10
14 8 7 14 8 7 2 8 7
– Do đó tổng số phép so sánh không vượt quá 2h. Alg: Build-Max-Heap(A) 1
4
– Vậy, thời gian tính là O(h) hay O(log n). i=2 i=1
1. n = length[A] 2 3
1 1 1
• Nếu viết trong ngôn ngữ chiều cao của đống, thì thời gian này 8
2 9 10
16 9 10 2
1 10
3
16
2
10
3
14
2 3
10
3. do Max-Heappify(A, i, n)
là O(h) 14 8 7 4 5 6 7 4 5 6 7 4 5 6 7
8
14 9 10
16 9 3 8
14 9 10
7 9 3 8
8 9 10
7 9 3
A: 4 1 3 2 16 9 10 14 8 7 2 8 7 2 8 1 2 4 1
43 68 43 68 43 68
6 77 33 9 6 77 33 9 6 77 33 9
13 13
11 19 99 6 23 89 2 14 11 19 99 6 23 89 2 14 11 19 99 6 23 89 2 14
Create PDF files without this message by purchasing novaPDF printer (http://www.novapdf.com)
Build-Min-Heap Build-Min-Heap Build-Min-Heap
43 68 43 68 43 68
6 77 33 9 6 77 33 9 6 77 33 9
13 13 13
11 19 99 6 23 22 2 14 11 19 99 6 23 22 2 14 11 19 99 6 3 22 2 14
43 68 43 68 43 68
6 77 33 9 6 77 33 9 6 77 33 9
13 13 13
11 19 99 6 3 22 2 14 11 19 99 6 3 22 2 14 11 19 7 6 3 22 2 14
43 68 43 68 43 68
6 77 33 9 6 77 33 9 6 77 33 9
13 13 13
11 19 7 6 3 22 2 14 11 19 7 6 3 22 2 14 1 19 7 6 3 22 2 14
Create PDF files without this message by purchasing novaPDF printer (http://www.novapdf.com)
Build-Min-Heap Build-Min-Heap Build-Min-Heap
43 68 43 68 43 68
6 77 33 9 6 77 33 2 6 77 33 2
13 13 13
1 19 7 6 3 22 2 14 1 19 7 6 3 22 9 14 1 19 7 6 3 22 9 14
43 68 43 68 43 68
6 77 3 2 6 77 3 2 6 77 3 2
13 13 13
1 19 7 6 33 22 9 14 1 19 7 6 33 22 9 14 1 19 7 6 23 22 9 14
43 68 43 68 43 68
6 77 3 2 6 6 3 2 6 6 3 2
13 13 13
1 19 7 6 23 22 9 14 1 19 7 77 23 22 9 14 1 19 7 77 23 22 9 14
Create PDF files without this message by purchasing novaPDF printer (http://www.novapdf.com)
Build-Min-Heap Build-Min-Heap Build-Min-Heap
43 68 43 68 43 68
6 6 3 2 6 6 3 2 1 6 3 2
13 13 13
1 19 7 12 23 22 9 14 1 19 7 12 23 22 9 14 6 19 7 12 23 22 9 14
43 68 43 68 43 68
1 6 3 2 1 6 3 2 1 6 3 2
13 13 13
6 19 7 12 23 22 9 14 5 19 7 12 23 22 9 14 5 19 7 12 23 22 9 14
43 2 43 2 43 2
1 6 3 68 1 6 3 68 1 6 3 9
13 13 13
5 19 7 12 23 22 9 14 5 19 7 12 23 22 9 14 5 19 7 12 23 22 68 14
Create PDF files without this message by purchasing novaPDF printer (http://www.novapdf.com)
Build-Min-Heap Build-Min-Heap Build-Min-Heap
43 2 1 2 1 2
1 6 3 9 43 6 3 9 43 6 3 9
13 13 13
5 19 7 12 23 22 68 14 5 19 7 12 23 22 68 14 5 19 7 12 23 22 68 14
1 2 1 2 1 2
5 6 3 9 5 6 3 9 5 6 3 9
13 13 13
43 19 7 12 23 22 68 14 43 19 7 12 23 22 68 14 6 19 7 12 23 22 68 14
1 2 87 2 87 2
5 6 3 9 5 6 3 9 5 6 3 9
13 13 13
6 19 7 12 23 22 68 14 6 19 7 12 23 22 68 14 6 19 7 12 23 22 68 14
Create PDF files without this message by purchasing novaPDF printer (http://www.novapdf.com)
Build-Min-Heap Build-Min-Heap Build-Min-Heap
5 2 5 2 5 2
87 6 3 9 87 6 3 9 6 6 3 9
13 13 13
6 19 7 12 23 22 68 14 6 19 7 12 23 22 68 14 87 19 7 12 23 22 68 14
5 2 5 2 5 2
5 6 3 9 5 6 3 9 5 6 3 9
13 13 13
87 19 7 12 23 22 68 14 11 19 7 12 23 22 68 14 11 19 7 12 23 22 68 14
11 43 27 35 99 42 77 71 33 67 89 87 43 27 35 99 42 77 71 33 67 89 87 43 27 35 99 42 77 71 33 67 89
Vun lại đống Vun lại đống Kết thúc
Thời gian tính của Buil-Max-Heap Thời gian tính của Buid-Max-Heap Thời gian tính của Build-Max-Heap
h
• Heapify đòi hỏi thời gian O(h) chi phí của Heapify ở nút i T (n) = ni hi (Chi phí Heapify tại mức i) × (số lượng nút trên mức này)
Alg: Build-Max-Heap(A)
là tỷ lệ với chiều cao của nút i trên cây i =0
h h
1. n = length[A] T (n) = ni hi = 2 i (h − i ) = O (n)
h
i=0 i =0
= 2 i (h − i ) Thay giá trị của ni và hi tính được ở trên
2. for i ← n/2 downto 1 Chiều cao Mức Số lượng nút i =0
O(n) h−i h
h
=
1
3. do Max-Heapify(A, i, n) O(log n) h0 = 3 (log n) i=0 20
2 Nhân cả tử và mẫu với 2h và viết 2 i là
h −i 2 −i
i=0 2
h1 = 2 i=1 21 h
k
Thời gian tính là: O(n log n) = 2h k Đổi biến: k = h - i
k =0 2
• Đánh giá này là không sát ! h2 = 1 i=2 22
k
n k
Thay tổng hữu hạn bởi tổng vô hạn
h3 = 0 i = 3 (log n) 23 k =0 2 và h = log n
= O(n) Tổng trên là nhỏ hơn 2
hi = h – i chiều cao của đống gốc tại mức i
ni = 2i số lượng nút ở mức i Thời gian tính của Build-Max-Heap: T(n) = O(n)
NGUYỄN ĐỨC NGHĨA NGUYỄN ĐỨC NGHĨA NGUYỄN ĐỨC NGHĨA
Create PDF files without this message by purchasing133novaPDF printer (http://www.novapdf.com)
Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN
134
Bộ môn KHMT - ĐHBKHN
135
5.5.2. Sắp xếp vun đống - Heapsort Ví dụ: A=[7, 4, 3, 1, 2] HeapSort(A)
• Sử dụng đống ta có thể phát triển thuật toán sắp xếp mảng.
• Sơ đồ của thuật toán được trình bày như sau: 1. Build-Max-Heap(A) O(n)
– Tạo đống max-heap từ mảng đã cho 2. for i ← length[A] downto 2
– Đổi chỗ gốc (phần tử lớn nhất) với phần tử cuối cùng trong mảng Max-Heapyfy(A, 1, 4) Max-Heapyfy(A, 1, 3) Max-Heapyfy(A, 1, 2) 3. do exchange A[1] ↔ A[i]
– Loại bỏ nút cuối cùng bằng cách giảm kích thước của đống đi 1 n-1 lần lặp
4. Max-Heapify(A, 1, i - 1) O(log n)
– Thực hiện Max-Heapify đối với gốc mới
– Lặp lại quá trình cho đến khi đống chỉ còn 1 nút
• Thời gian tính: O(n log n)
• Có thể chứng minh thời gian tính là Θ(n log n)
Max-Heapyfy(A, 1, 1)
NGUYỄN ĐỨC NGHĨA 136 NGUYỄN ĐỨC NGHĨA 137 NGUYỄN ĐỨC NGHĨA 138
Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN
Tổng kết:
Câu hỏi Có thể sắp xếp nhanh đến mức độ nào? Các thuật toán sắp xếp dựa trên phép so sánh
• Giả sử các số trong max-heap là phân biệt, phần tử lớn thứ hai • Heapsort, Mergesort, và Quicksort có thời gian O(n log n) là
nằm ở đâu? các thuật toán có thời gian tính tốt nhất trong các thuật toán Name Average Worst In place Stable
trình bày ở trên
Bubble sort — O(n²) Yes Yes
• Liệu có thể phát triển được thuật toán tốt hơn không?
• Câu trả lời là: "Không, nếu như phép toán cơ bản là phép so Selection sort O(n²) O(n²) Yes No
sánh". Insertion sort O(n + d) O(n²) Yes Yes
NGUYỄN ĐỨC NGHĨA 139 NGUYỄN ĐỨC NGHĨA 140 NGUYỄN ĐỨC NGHĨA 141
Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN Bộ môn KHMT - ĐHBKHN
QUESTIONS?