Nội dung
Chọn trực tiếp (Selection Sort)
Chèn trực tiếp (Insertion Sort)
Nổi bọt (Bubble Sort)
Merge Sort
Quick Sort
Heap Sort
Radix SortKhái niệm
Sắp thứ tự:
Đầu vào: một mảng
Đầu ra: mảng có thứ tự tăng (hoặc giảm) trên khóa
Phân loại:
Sắp thứ tự ngoại (external sort): tập tin
Sắp thứ tự nội (internal sort): bộ nhớ
Giả thiết:
Sắp thứ tự nội
Sắp tăng dần
65 trang |
Chia sẻ: Thục Anh | Ngày: 11/05/2022 | Lượt xem: 514 | Lượt tải: 0
Bạn đang xem trước 20 trang nội dung tài liệu Bài giảng Cấu trúc dữ liệu - Chương 3+4: Sắp xếp - Lê Minh Trung, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
TS. Lê Minh Trung
Th.S Lương Trần Ngọc Khiết
Khoa CNTT, Đại học Sư phạm TP. HCM
Nội dung
Chọn trực tiếp (Selection Sort)
Chèn trực tiếp (Insertion Sort)
Nổi bọt (Bubble Sort)
Merge Sort
Quick Sort
Heap Sort
Radix Sort
Khái niệm
Sắp thứ tự:
Đầu vào: một mảng
Đầu ra: mảng có thứ tự tăng (hoặc giảm) trên khóa
Phân loại:
Sắp thứ tự ngoại (external sort): tập tin
Sắp thứ tự nội (internal sort): bộ nhớ
Giả thiết:
Sắp thứ tự nội
Sắp tăng dần
Chọn trực tiếp
Input: int a[n]
Output: mảng đã được sắp xếp
Ý tưởng:
Với mỗi i=0, 1, 2,, n-2
Tìm phần tử nhỏ nhất của mảng con a[i..n-1] và hoán vị phần
tử này với a[i].
23 78 45 8 32 56
8 78 45 23 32 56
8 23 45 78 32 56
8 23 32 78 45 56
8 23 32 45 78 56
8 23 32 45 56 78
Original List
After pass 1
After pass 2
After pass 3
After pass 4
After pass 5
Sorted Unsorted
Chọn trực tiếp
Chọn trực tiếp
void SelectionSort(int a[], int n){
for(int i=0;i<=n-2;i++){
int iMin =i;
for(int j=i+1;j<n;j++){
if(a[j] <a[iMin])iMin =j;
}
int t= a[i]; a[i] = a[iMin]; a[iMin]=t;
}
}
Đánh giá chọn trực tiếp
Vòng lặp for ngoài có n-1 lần lặp i=0,1,,n-2
Số phép so sánh của lần lặp thứ i: n-i-1
Số phép so sánh:
𝑆𝑆 = 𝑖=0
𝑛−2 𝑛 − 𝑖 − 1 =𝑛 − 1 + 𝑛 − 2 +⋯+ 1 =
𝑛−1 𝑛
2
Độ phức tạp của thuật toán: 𝑂 𝑛2
Chèn trực tiếp (Insertion Sort)
Input: int a[n]
Output: mảng đã được sắp xếp
Ý tưởng:
Với mỗi i=1,2,,n-1
Mảng con a[0..i-1] đã được sắp thứ tự tăng, chèn a[i] vào vị
trí thích hợp để mảng con a[0..i] sắp thứ tự tăng.
Original List
After pass 1
After pass 2
After pass 3
After pass 4
After pass 5
23 78 45 8 32 56
23 78 45 8 32 56
23 45 78 8 32 56
8 23 45 78 32 56
8 23 32 45 78 56
8 23 32 45 56 78
Sorted Unsorted
Chèn trực tiếp
Chèn trực tiếp
void InsertionSort(int a[], int n){
for(int i=1;i<n;i++){
int x = a[i];
int j= i-1;
while(a[j]>x){
a[j+1]=a[j];j--; if(j==-1)break;
}
a[j+1]=x;
}
}
Đánh giá chèn trực tiếp
Vòng lặp for ngoài có n-1 lần lặp i=1,2,,n-1
Tốt nhất
𝑆𝑆 = 𝑖=1
𝑛−11 = 𝑛 − 1 ≈ 𝑂(𝑛)
Xấu nhất
𝑆𝑆 = 𝑖=1
𝑛−12𝑖 = 2 1 + 2 +⋯+ 𝑛 − 1 = 𝑛(𝑛 − 1) ≈ 𝑂(𝑛2)
Trung bình
𝑆𝑆 ≈ 𝑂(𝑛2)
Nổi bọt (Bubble Sort)
Input: int a[n]
Output: mảng đã được sắp xếp
Ý tưởng:
Với mỗi i=n-1, n-2,, 1
Với mỗi j=0, 1,, i
Nếu a[j]<a[j-1] thì swap(a[j],a[j-1])
Nổi bọt
6 4 7 2 3
4 6 2 3 7
Bước 1
Bước 2
Bước 3
Bước 4
4 2 3 6 7
2 3 4 6 7
sorted
sorted
sorted
sorted
Nổi bọt
void BubbleSort(int a[],int n){
for(int i=n-1;i>=1;i--){
for(int j=1; j<=i;j++)
if(a[j-1]>a[j]){
int temp =a[j-1];
a[j-1]=a[j];
a[j] = temp;
}
}
}
Đánh giá Bubble Sort
Vòng lặp for ngoài có n-1 lần lặp i=n-1, n-2, , 2, 1
Vòng lặp for trong có i lần lặp j=1, 2, , i
Mỗi lần lặp có 1 phép so sánh, số phép so sánh:
𝑆𝑆 = 𝑖=1
𝑛−1 𝑖 = 1 + 2 +⋯+ 𝑛 − 1 =
𝑛−1 𝑛
2
≈ 𝑂(𝑛2)
Độ phức tạp của thuật toán: 𝑂(𝑛2)
Cải tiến Bubble Sort
Cho phép nổi bọt theo hai chiều
Nổi bọt xuống, nổi bọt lên
Khi nổi bọt ghi nhận lại
Có sự nổi bọt thật sự hay không
exchange =false mảng đã được sắp thứ tụ
Vị trí nổi bọt cuối cùng
Bubble Sort cải tiến
void BubbleSort(int a[],int n){
int iMax=n-1, jMax=0,t,k;
bool exchange = true;
do
{
exchange =false;
for(int i=jMax;i<iMax;i++) //nổi bọt xuống
if(a[i]>a[i+1]){t=a[i]; a[i]=a[i+1];
a[i+1]=t;exchange=true;k=i;}
iMax =k;
if(!exchange)break; //nếu không có hoán vị nào trước đó thì
thoát vì mảng đã được sắp thứ tự
exchange = false;
for(int i=iMax;i>jMax;i--) //nổi bọt lên
if(a[i]<a[i-1]){t=a[i];a[i]=a[i-1];
a[i-1]=t; exchange=true; k=i;}
jMax=k;
}while(exchange);
}
Chia để trị
Ý tưởng:
Chia danh sách ra làm 2 phần
Sắp thứ tự riêng cho từng phần
Trộn 2 danh sách riêng đó thành danh sách có thứ tự
Hai giải thuật:
Merge sort:
Chia đều thành 2 danh sách
Sắp thứ tự riêng
Trộn lại
Quick sort:
Chia thành 3 phần: nhỏ, giữa (pivot), lớn
Sắp thứ tự riêng
Đánh giá sơ lược giải thuật chia để trị
Giả sử 2 danh sách có số phần tử là n’ = n/2
Dùng insertion sort riêng cho từng mảnh
Trộn 2 danh sách tốn (n’ + n’) = n lần so sánh
Số lần so sánh tổng cộng: 2*((n/2)2/2 + O(n/2)) + n =
n2/4 + O(n)
So sánh với insertion sort là n2/2 + O(n)
Có vẻ nhanh hơn
Merge sort
Start
26 33 29 35
26 29 33 35 12 19 22
12 19 22 26 29 33 35
12 19
Finish
26 33 35 29 19 12 22
26 33
26 33 35 29
35 29
26 33 35 29 19 12 22
19 12
19 12 22
Trộn Trộn
Trộn
Trộn
Trộn
Trộn
Trộn hai mảng con tăng
void Merge(int a[], int first, int last){
int mid = (first +last)/2;
int first1= first, last1=mid;
int first2=mid+1, last2=last;
int temp[MAX_SIZE];
int i=first1, j=first2, k=0;
while ((i<=last1)&&(j<=last2))
{
if(a[i]<=a[j])temp[k++]=a[i++];
else temp[k++] = a[j++];
}
if(i>last1) //mảng bên trái hết phần tử
while(j<=last2)temp[k++]=a[j++];
if(j>last2) //mảng bên phải hết phần tử
while(i<=last1)temp[k++]=a[i++];
for(i=first;i<=last;i++)a[i]=temp[i-first]; //copy mảng tạm vào
a[first..last]
}
Merge Sort
void MergeSort(int a[], int first, int last){
if(first<last){//mảng con có nhiều hơn một
phần tử
int mid = (first +last)/2;
MergeSort(a,first,mid);
MergeSort(a, mid+1, last);
Merge(a,first, last);
}
}
Merge Sort – Ví dụ
6 3 9 1 5 4 7 2
5 4 7 26 3 9 1
6 3 9 1 7 25 4
6 3 19 5 4 27
3 6 1 9 2 74 5
2 4 5 71 3 6 9
1 2 3 4 5 7 8 9
divide
dividedividedivide
dividedivide
divide
merge merge
merge
merge
merge merge
merge
Phân tích thuật toán Merge
Trường hợp xấu nhất
Phân tích thuật toán Merge
Trộn hai mảng kích cỡ k
Tốt nhất:
Tất cả phần tử trong mảng trái nhỏ hơn phần tử trong mảng phải.
Số phép so sánh: k + 1
Xấu nhất:
i = k-1 và j = k-1
Số phép so sánh: 2k
...... ......
......
0 k-1 0 k-1
0 2k-1
Phân tích Merge Sort
Các cấp gọi đệ qui của mảng có 8 phần tử
Phân tích Merge Sort
.
.
.
.
.
.
. . . . . . . . . . . . . . . . .
2m
2m-1 2
m-1
2m-2 2m-2 2m-2 2m-2
20 2
0
level 0 : 1 merge (size 2m-1)
level 1 : 2 merges (size 2m-2)
level 2 : 4 merges (size 2m-3)
level m
level m-1 : 2m-1 merges (size 20)
Đánh giá Merge Sort
Xấu nhất
Số phép so sánh:
= (2*20 )*2m-1 + (2*21)*2m-2 + ... + (2*2m-1)*20
= 2m + 2m + ... + 2m ( m hạng tử )
= m*2m
Thay m = log n
Số phép so sánh:
= n * log2n
O (n * log2n )
Đánh giá Merge Sort
Merge Sort là thuật toán hiệu quả xét trên phương
diện độ phức tạp tính toán và thời gian.
Độ phức tạp trong cả trường hợp tốt nhất và xấu nhất: O (n * log2n )
Để trộn hai mảng con của một mảng cho trước, đòi
hỏi một mảng có kích cỡ bằng kích cỡ mảng ban
đầu .
Quick Sort
Sort (26, 33, 35, 29, 19, 12, 22)
Sort (19, 12, 22)
Sort (33, 35, 29)
Combine into (12, 19, 22, 26, 29, 33, 35)
Phân thành (19, 12, 22) và (33,35,29) với pivot=26
Phân thành (12) và (22) với pivot=19
Phân thành (29) và (35) với pivot=33
Sort (12)
Sort (22)
Combine into (12, 19, 22)
Sort (29)
Sort (35)
Combine into (29, 33, 35)
Quick Sort
Algorithm Quick Sort
Input: mảng cần sắp
Output: mảng đã được sắp xếp
1. if (có ít nhất 2 phần tử)
//Phân hoạch mảngthành 3 phần:
//- Phần nhỏ hơn phần tử giữa
//- Phần tử giữa
//- Phần lớn hơn phần tử giữa
1.1. Phân hoạch mảng thành 3 phần
1.2. Call QuickSort cho phần bên trái
1.3. Call QickSort cho phần bên phải
//Chỉ cần ghép lại là thành danh sách có thứ tự
End
Quick Sort
void QuickSort(int a[], int low, int high){
if(low<high){
int pivot_pos = Partition(a, low, high);
QuickSort(a,low,pivot_pos);
QuickSort(a, pivot_pos+1, high);
}
}
Phân hoạch cho Quick Sort
Phân hoạch cho Quick Sort
Partition Quick Sort
int Partition(int a[], int low, int high){
int mid = (low + high)/2;
Swap(a[low], a[mid]);
int pivot = a[low];
int last_small = low;
for(int i= low+1; i<=high; i++)
if(a[i]<pivot){
last_small ++; Swap(a[last_small],a[i]);
}
Swap(a[low],a[last_small]);
return last_small;
}
Ví dụ Quick Sort
19 35 33 26 29 12 22
0 1 2 3 4 5 6
low=0 high=6
mid=(low+high)/2=3
QuickSort(0,6)
pivot_position = Partition(0,6)= 3
pivot
last_small
index
26
pivot_position
QuickSort(0,2)
QuickSort(4,6)
Ví dụ Quick Sort
22 19 12 26 29 33 35
0 1 2 3 4 5 6
low=0 high=2
mid=(low+high)/2=1
QuickSort(0,2)
pivot_position = Partition(0,2) = 1
last_small
index
pivot
19
pivot_position
QuickSort(0,0)
QuickSort(2,2)
(Không làm gì cả)
Ví dụ Quick Sort
29 33 352612 19 22
0 1 2 3 4 5 6
low=4 high=6
mid=(low+high)/2=5
QuickSort(4,6)
pivot_position = Partition(4,6)= 5
last_small
index
pivot
33
pivot_position
QuickSort(4,4)
QuickSort(6,6)
(Không làm gì cả)
Các trường hợp của Quick sort
Đánh giá Quick Sort
Trường hợp xấu nhất:
Một bên rỗng và một bên là n-1 phần tử => n(n-1)/2
Chọn phần tử pivot:
Đầu (hay cuối): trường hợp xấu xảy ra khi danh sách đang
có thứ tự (hoặc thứ tự ngược)
Ở trung tâm, hoặc ngẫu nhiên: trường hợp xấu khó xảy ra
Trường hợp trung bình:
C(n) = 2n ln n + O(n) ≈ 1.39 n lg n + O(n) ≈O(nlogn)
Heap và Heap Sort
Heap (định nghĩa lại):
Mảng có n phần tử (từ 0 đến n-1)
ak ≥ a2k+1 và ak ≥ a2k+2 (ak lớn nhất trong 3 phần tử)
Đặc tính:
a0 là phần tử lớn nhất
Heap chưa chắc có thứ tự
Nửa sau của mảng bất kỳ thỏa định nghĩa heap
Heap sort:
Lấy a0 ra, tái tạo lại heap => Phần tử lớn nhất
Lấy a0 mới ra, tái tạo lại heap => Phần tử lớn kề
Heap Sort
void HeapSort(int a[], int n){
//xây dựng heap ban đâu
BuildHeap(a,n); //mảng đã là một heap
for(int i=n-1; i>=1; i--){
int current = a[i];
a[i]= a[0]; //chuyển phần tử lớn nhất về cuối
//a[1..i-1] đang là heap, thêm current vào để
vẫn là heap
InsertHeap(current,a,0,i-1);
}
}
Biểu diễn Heap
Dạng cây nhị phân:
Node gốc là a0
2 node con của phần tử
ak là 2 phần tử a2k+1 và
a2k+2
Ở mức cuối cùng, các
node lấp đầy từ bên trái
sang bên phải (cây nhị
phân gần đầy đủ)
0 1 2 3 4 5 6 7 8
Ví dụ Heap Sort
y r p d f b k a c
0 1 2 3 4 5 6 7 8
y
r p
d f b k
a c
current
Ví dụ Heap Sort
r f p d c b k a y
0 1 2 3 4 5 6 7 8
r
f p
d c b k
a y
current
Ví dụ Heap Sort
p f k d c b a r y
0 1 2 3 4 5 6 7 8
p
f k
d c b a
r y
current
Ví dụ Heap Sort
k f b d c a p r y
0 1 2 3 4 5 6 7 8
k
f b
d c a p
r y
current
Ví dụ Heap Sort
f d b a c k p r y
0 1 2 3 4 5 6 7 8
f
d b
a c k p
r y
current
Ví dụ Heap Sort
d c b a f k p r y
0 1 2 3 4 5 6 7 8
d
c b
a f k p
r y
current
Ví dụ Heap Sort
c a b d f k p r y
0 1 2 3 4 5 6 7 8
c
a b
d f k p
r y
current
Ví dụ Heap Sort
b a c d f k p r y
0 1 2 3 4 5 6 7 8
b
a c
d f k p
r y
current
Algorithm InsertHeap
Input: mảng là heap trong khoảng từ low+1 đến high, current là giá trị
cần thêm vào
Output: mảng là heap trong khoảng từ low đến high
1. Bắt đầu kiểm tra tại vị trí low
2. while (chưa kiểm tra xong đến high)
2.1. Tìm lớn nhất trong bộ ba phần tử current, a[2k+1], a[2k+2]
2.2. if (phần tử đó là current)
2.2.1. Ngừng vòng lặp
2.3. else
2.3.1. Dời phần tử lớn nhất lên vị trí hiện tại
2.3.2. Kiểm tra bắt đầu từ vị trí của phần tử lớn nhất này
3. Đưa current vào vị trí đang kiểm tra
End
Giải thuật tái tạo lại Heap
Giải thuật tái tạo Heap
Input: InsertHeap(current, a[], low, high)
Mảng a[low+1high] đã là một heap
Output: thêm current vào sao cho mảng a[lowhigh]
vẫn là một heap
void InsertHeap(int current,int a[], int low, int high)
{
int large = 2*low +1;
while(large <=high){//chưa xét hết phần tử
if(large<high) //có đủ hai con
if(a[large +1]>a[large])large++;
if(current>=a[large])break;
a[low]=a[large]; //di chuyển phần tử max
low = large;
large = 2*low +1;
}
a[low]=current; //chuyển current vào vị trí đang xét
}
Xây dựng Heap ban đầu
void BuildHeap(int a[],int n){
//nửa sau từ n/2 --> n-1 đã là một heap
for(int i=n/2-1; i>=0; i--){
int current = a[i];
InsertHeap(current,a,i,n-1);
}
}
Ví dụ xây dựng Heap ban đầu
p c y d f b k a r
0 1 2 3 4 5 6 7 8
Bước 1
p c y r f b k a d
0 1 2 3 4 5 6 7 8
Bước 2
p c y r f b k a d
0 1 2 3 4 5 6 7 8
Bước 3
p r y c f b k a d
0 1 2 3 4 5 6 7 8
Bước 3’
p r y d f b k a c
0 1 2 3 4 5 6 7 8
Bước 4
y r p d f b k a c
0 1 2 3 4 5 6 7 8
Đánh giá Heap Sort
Trường hợp xấu nhất và trung bình:
C = 2n lg n + O(n)
M = n lg n + O(n)
So với Quick Sort
Trung bình: chậm hơn quick sort
Xấu nhất: O(n lg n) < n(n-1)/2
Radix Sort
Radix sort khác với các phương pháp sắp xếp khác.
Radix sort
Xem các phần tử như các chuỗi
Nhóm các phần tử có cùng kí tự tận cùng phải
Nhập các nhóm lại với nhau
Lập lại bước trên cho tất cả các vị trí từ vị trí tận cùng
phải (rightmost) cho đến vị trí tận cùng trái (leftmost)
mom, dad, god, fat, bad, cat, mad, pat, bar, him original list
(dad,god,bad,mad) (mom,him) (bar) (fat,cat,pat) group strings by
rightmost letter
dad,god,bad,mad,mom,him,bar,fat,cat,pat combine groups
(dad,bad,mad,bar,fat,cat,pat) (him) (god,mom) group strings by middle letter
dad,bad,mad,bar,fat,cat,pat,him,god,mom combine groups
(bad,bar) (cat) (dad) (fat) (god) (him) (mad,mom) (pat) group strings by first
letter
bad,bar,cat,dad,fat,god,him,mad,mom,par combine groups
(SORTED)
Radix Sort – Ví dụ
Radix Sort – Ví dụ
void RadixSort(inout theArray:ItemArray, in n:integer, in
d:integer)
// sort n d-digit integers in the array theArray
for (j=d; j>=1; j--) {
Khởi tạo 10 nhóm bằng rỗng
Khởi tạo các counter của các nhóm bằng 0
for (i=0; i<n; i++) {
k = chữ số thứ j của theArray[i]
Đặt theArrar[i] vào nhóm thứ k
Tăng counter thứ k thêm 1
}
Thay thế các phần tử trong theArray bởi các phần tử
trong nhóm 1, nhóm 2, theo thứ tự
}
Radix Sort - Algorithm
Đánh giá Radix Sort
Đòi hỏi 2*n*d phép gán để sắp n chuỗi có chiều dài là d
Độ phức tạp O(n)
So sánh các giải thuật
Luyện tập
Trình bày cách thực hiện sắp xếp mảng sau theo
các thuật toán (ghi kết quả sau từng bước)
Selection Sort
Insertion Sort
Buble Sort
Merge Sort
Quick Sort
A={10, 3, 7, 6, 2, 5, 4, 16, 20, 1 }
Các file đính kèm theo tài liệu này:
- bai_giang_cau_truc_du_lieu_chuong_34_sap_xep_le_minh_trung.pdf