Thuật toán Selection sort sắp xếp mảng bằng cách lần lượt chọn các phần tử nhỏ nhất chưa được sắp xếp trong mảng để đưa các phần tử vào vị trí đúng của nó.. Thuật toán dựa vào bài toán t
Trang 1TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
KHOA CÔNG NGHỆ THÔNG TIN
Subject: DATA STRUCTURE and ALGORITHMS
Lab 03: Sorting Algorithms
Class: 20CLC01
ID group: 01
Đỗ Thụy Phương Vy Nguyễn Lê Sơn Dương Thanh Giang Trần Nguyễn Lan Trinh
Trang 2BẢNG ĐÁNH GIÁ THÀNH VIÊN Môn Cấu trúc dữ liệu & Giải thuật – Lớp 20CLC01
Giảng viên hướng dẫn:
Nguyễn Thanh Phương Nguyễn Ngọc Thảo Bùi Huy Thông Người thực hiện: Tất cả các thành viên trong nhóm
ID nhóm thực hiện: 01
% đóng góp của nhiệm
vụ vào tổng thể bài làm
Đánh giá két quả nhiệm vụ
1 20127098 Đỗ Thụy Phương Vy
Selection Sort* (20), Flash Sort***
(20), Comparision-Experimental run (Table fill) (40), Command 1+2 (30)
Insertion Sort* (20), Shell Sort** (20), Counting Sort*** (20), Comparision- Chart draw and comment (2 cái cuối) (20), Run time-Experimental run (Table fill) (2 cái đầu) (20), Command
4 (15)
3 20127486 Dương Thanh Giang
Bubble Sort* (20), Shaker Sort* (20), Radix Sort*** (20), Comparision- Chart draw and comment (2 cái đầu) (20), Run time-Experimental run (Table fill) (2 cái cuối) (20), Command
3 (15)
4 20127653 Trần Nguyễn Lan Trinh
Merge Sort** (20), Quick Sort** (20), Heap Sort** (20), Run time-Chart draw and comment (40), Command 5
(15)
1 Giới thiệu
Xin chào thầy cô, nhóm chúng em là nhóm 01 đến từ lớp
20CLC01 Hôm nay chúng em mang đến cho thầy cô đồ án
về đo độ phức tạp của thuật toán Mục đích của thuật toán là
để tính toán, chạy thuật toán và đo thời gian chạy của một
thuật toán, sau cùng dựa trên những kết quả có được và đưa ra
Trang 3những kết luận về các thuật toán đó Nhóm của chúng em
chọn Set 2 (bao gồm 11 thuật toán): Selection Sort, Insertion
Sort, Bubble Sort, Shaker Sort, Shell Sort, Heap Sort, Merge Sort, Quick Sort, Counting Sort, Radix Sort and Flash Sort.
Sau quá trình học hỏi, tìm hiểu và tiếp thu thêm rất nhiều kiến thức mới, chúng em đã cùng nhau hoàn thành trọn vẹn đồ án
mà giảng viên đã giao Chúng em hiểu được những nguyên
lí cơ bản của những thuật toán sắp xếp, học được cách làm sao để đo được một thuật toán và tính toán được đầu vào và đầu ra, hiểu được trường hợp tốt nhất và trường hợp xấu nhất khi đưa một đầu vào vào
2 Cấu hình máy tính đã được sử dụng
CPU: Intel® Core™ i7-10750H CPU @ 2.60GHz
SSD: 512GB
RAM: 16GB DDR4
GPU: NVIDIA GeForce GTX 1650 Ti 4GB
CPU: AMD Ryzen 7 3750H with Radeon Vega Mobile Gfx
SSD: 256GB
RAM: 8GB DDR4
GPU: NVIDIA GeForce GTX 1650 4GB
CPU: Intel® Core ™ i5 – 7200U CPU @ 2.50GHz
Trang 4Thuật toán Selection
sort sắp xếp mảng bằng cách lần lượt chọn các phần tử nhỏ nhất chưa được sắp xếp trong mảng để đưa các phần tử vào vị trí đúng của nó Thuật toán dựa vào bài toán tìm phần tử nhỏ nhất và đổi chỗ các phần tử với nhau để đưa phân tử vào vị trí đúng của nó
- Bước 1 : Khởi tạo minVal = A[i]minVal = A[i], minInx = i
- Bước 2 : So sánh với các phần tử trong đoạn [i+1, n−1] Nếu
A[j]< minVal thì minVal = a[j], minInx = j
Trang 5- Bước 3: Hoán vị A[i] và phần tử nhỏ nhất trong đoạn vừa tìm được
- Bước 4: Lặp lại các bước trên đến hết mảng
Mã giả:
For i = 0 to n-2
minVal = A[i]
minInx = i For j = i+1 to n-1
If A[j] < minVal then
minVal = A[j]
minInx = j endif
endfor A[minInx] = A[i]
A[i] = minVal endfor
Đánh giá độ phức tạp:
- Thời gian: O(n2)
- Không gian: O(1)
2 phần: một danh sách con đã được sắp xếp và phần khác l
à các phần tử không có thứ tự Giải thuật sắp xếp chèn sẽ thực hiện việc tìm kiếm liên tiếp qua mảng đó, và các phần
Trang 6tử không có thứ tự sẽ được di chuyển và được chèn vào vị trí thích hợp trong danh sách con (của cùng mảng đó)
Mô tả: Để sắp xếp một mảng bất kì có kích thước n
theo thứ tự tăng dần thì:
1: Lặp lại từ arr [1] đến arr[n] trên mảng 2: So sánh phần tử hiện tại với phần tử phía trước nó 3: Nếu phần tử chính nhỏ hơn phần tử trước nó, thì tiếp tục l
ấy phần tử chính so sánh với các phần tử trước đó rồi di chuy
ển các phần tử lớn hơn lên một vị trí để tạo chỗ cho phần tử chính đang hoán đổi
Các bước làm:
Bước 1: Tạo vòng lặp từ vị trí thứ hai đến vị trí thứ n(để vị tr
í thứ nhất làm nơi so sánh cho vị trí thứ hai)
Bước 2: Tạo phần mảng với sắp xếp có 1 phần tử (phần tử đ
Bước 4: Tiếp tục lặp lại bước trên với những phần tử tiếp th
eo cho đến khi duyệt qua hết tất cả phần tử của mảng
Ví dụ thuật toán sắp xếp - (theo GeeksforGeeks)
Trang 7 Mã giả:
Trang 8 Đánh giá độ phức tạp
- Theo thời gian : + Trường hợp tốt nhất: O(n) : khi các phần tử đã được sắp xếp
Trang 9- Xuất phát từ phần tử cuối danh sách ta tiến hành so sánh với phần tử bên trái của nó Nếu phần tử đang xét có khóa nhỏ hơnphần tử bên trái của nó ta tiến đưa nó về bên trái của dãy bằng cách hoán vị với phần tử bên trái của nó Tiếp tục thực hiện như thế đối với bài toán có n phần tử thì sau n –1 bước ta thu được danh sách tăng dần.
Mô tả:
- Xét lần lượt 2 phần tử liền kề nhau bắt đầu từ cuối mảng
Nếu 2 phần tử này là nghịch thế của nhau a[i] > a[i – 1], tiến hành hoán vị đổi chỗ hai phần tử này
Lặp lại điều trên sẽ đem được phần tử nhỏ nhất lên đầu mảng và loại phần tử này ra khỏi mảng đang xét
Tiếp tục thực hiện lại các bước trên đối với mảng mới để đưa dần các phần tử nhỏ nhất về đầu mảng
Thực hiện lại các thao tác cho đến khi mảng đang xétchỉ còn 1 phần tử duy nhất
Các bước:
- Bước 1 : i=0; //Phần tử đầu tiên
- Bước 2: Lần lượt so sánh và đổi chổ (nếu cần) từ phải sang
trái đối với các phần từ từ a[n] đến a[i] với biến gán j=n-i và lặp lại khi j>i
- Bước 3 : i=i+1
- Bước 4 : Nếu i < n, quay lại Bước 2.
Ngược lại, dừng, dãy đã cho đã sắp xếp đúng vị trí
Mã giả:
Trang 10- Shaker Sort là một cải tiến của Bubble Sort.
- Sau khi đưa phần tử nhỏ nhất về đầu mảng sẽ đưa phần tử lớnnhất về cuối dãy Do đưa các phần tử về đúng vị trí ở cả hai đầu nên Shaker Sort sẽ giúp cải thiện thời gian sắp xếp dãy
số do giảm được độ lớn của mảng đang xét ở lần so sánh kế tiếp
Trang 11Bước 2a: j = r; // đẩy phần tử nhỏ về đầu
mảng
Trong khi (j > l) thực hiện
Nếu a[j] < a[j-1]: a[j] ↔ a[j-1];
k = j; // lưu lại nơi xảy ra
hoán vị
j = j – 1;
l = k; // loại các phần tử đã có thứ tự ở đầu
dãy
- Bước 2b: j = l; // đẩy phần tử lớn về cuối mảng
Trong khi (j < r) thực hiện
Nếu a[j] > a[j-1]: a[j] ↔ a[j+1];
k = j; // lưu lại nơi xảy ra
Trang 12 Ý tưởng: Shell Sort là một giải thuật sắp xếp mang lại
hiệu quả cao dựa trên giải thuật sắp xếp chèn (Insertion Sort) Giải thuật này tránh các trường hợp phải tráo đổi
vị trí của hai phần tử xa nhau
trong giải thuật sắp xếp chọn (nếu như phần tử nhỏ hơn
ở vị trí bên phải khá xa so với phần tử lớn hơn
bên trái)
Trang 13 Mô tả:
Shellsort hoạt động bằng cách sắp xếp các phần tử nằm xa nhau, sau đó
dần rút ngắn khoảng cách sắp xếp(gap), đều này giúp di chuyển các phầ
n tử đi một khoảng cách xa có thể giúp các phần tử đi về vị trí chính xác của mình hiệu quả hơn so với việc di chuyển qua từng phần tử liền kề.
- Một số trình tự tối ưu được sử dụng là:
Trình tự ban đầu của Shell: N/2, N/4,…,1
Tăng dần Knuth: 1, 4, 13,…,(3k-1)/2
Số gia tăng của Sedgewick: 1, 8, 23, 77, 281, 1073, 4193, 16577…4j+1+3·2j+1
Số gia tăng của Hibbard: 1, 3, 7, 15, 31, 63, 127, 255, 511…
Số gia tăng của Papernov & Stasevich: 1, 3, 5, 9, 17, 33, 65,
…
Pratt: 1, 2, 3, 4, 6, 9, 8, 12, 18, 27, 16, 24, 36, 54, 81 …
- Để diễn giải các bước thực hiện thì chúng ta sẽ có ví dụ để dễ hình dung. Giả sử chúng ta cần sắp xếp mảng phía dưới theo shell sort:
*Bước 1 : Chúng ta sẽ sử dụng trình tự ban đầu của shell (N/2, N/4,…
1) làm khoảng cách trong thuật toán
-Trong vòng lặp đầu tiên, nếu kích thước mảng là N=8 thì các phần
tử nằm trong khoảng N/2=4 sẽ được so sánh và hoán đổi vị trí nếu chúng không theo thứ tự
- Phần tử thứ 0 được so sánh với phần tử thứ 4
Trang 14- Nếu phần tử thứ 0 lớn hơn phần tử thứ 4 thì phần tử thứ 4 được lư
u trữ đầu tiên trong biến tạm thời và phần tử thứ 0
(tức là phần tử lớn hơn) được lưu trữ ở vị trí thứ 4 và phần tử được lưu trữ trong tạm thời được lưu trữ ở vị trí thứ 0
(Tức là hoán đổi hai phần tử cho nhau)
- Quá trình này tiếp tục cho tất cả các phần tử còn lại
Sau khi sắp xếp, mảng sẽ như thế này:
* Bước 2 :
Trong vòng lặp thứ hai, một khoảng N/4=8/4=2 được thực hiện và một l
ần nữa các phần tử nằm trong các khoảng này được sắp xếp
Trang 15Sau khi sắp xếp, mảng sẽ như thế này:
* Bước 3 : Cuối cùng, khi khoảng là N/8 = 8/8 =
1 thì các phần tử của mảng nằm trong khoảng 1 được sắp xếp Shell Sort
sử dụng giải thuật sắp xếp chèn để sắp xếp mảng
Trang 17-> Tóm lại với shell sort, chúng ta có nhiều cách sử dụng khi dùng n hiều cách tính khoảng cách với nhau
nhưng tất cả sẽ được thực hiện một cách tổng quát như sau:
Bước 1: Sử dụng vòng lặp với khoảng cách giảm dần giữa các phần tử s
ắp xếp để tách ra các phần tử để sắp xếp
Bước 2 : Sử dụng vòng lặp với mỗi khoảng được chia
trong mảng rồi sắp xếp tất cả phần tử trong khoảng ấy
Bước 3: Tiếp tục với việc giảm khoảng cách dần dần để tách ra các kho
ảng rồi sắp xếp cho đến khi khoảng cách = 1
Bước 4: Khi khoảng cách = 1, dùng Insertion sort để sắp xếp lại
Source code: (theo việc dùng trình tự ban đầu của Shell: N/2, N/4,…,1)
Đánh giá độ phức tạp
- Theo thời gian : do có nhiều cách tính khoảng cách khác nhau
nên thời gian sẽ khác nhau
+ Trường hợp tốt nhất: O(nlognnlog n) – O (nlog2nnlog2 n )
+Trung bình: tùy theo cách chia khoảng cách
+Trường hợp xấu: O(nlog2nnlog2 n) – O(n2n2)
Trang 18trong trường hợp tệ nhất của các khoảng cách trong Shell sort (theo Wiki)
- Không gian bộ nhớ sử dụng : O(1),O(n)
Ứng dụng:
- Việc gọi một ngăn xếp là tốn kém
Thư viện uClibc sử dụng kiểu thuật toán sắp xếp này
- Kỹ thuật đệ quy vượt quá giới hạn cho phép Trình nén bzip2 sử dụng kiểu sắp xếp này
- Sắp xếp chèn không hoạt động tốt khi các phần tử gần bằng nhau nằm
Trang 19Theo Wikipedia, một Binary
Heap được gọi là hoàn chỉnh khi trong đó các mục được lưu trữ the
o một thứ tự đặc biệt sao cho giá trị trong nút cha lớn hơn( nếu là Max Heap) hoặc nhỏ hơn(nếu là Min Heap)
so với giá trị trong hai nút con của nó
Binary Heap có thể được minh họa bằng ảnh sau:
Nguồn ảnh: Wikipedia
Một Heap được biểu diễn dưới dạng mảng có:
+ Gốc của cây là a[0]
+ Với một nút cha a[i], con trái của a[i] (nếu có) là a[2*i + 1] và con phải của a[i] (nếu có) là a[2*i + 2]
Max Heap: a[cha] >= a[con]
Min Heap: a[cha] <= a[con]
Cách tính số phần tử trong Heap có con được dựa theo công thức: n/2 Các phần tử còn lại không có con
Mô tả:
Trang 20Để sắp xếp một mảng theo thứ tự, ta cần xây dựng cấu trúc Binary Heap để tìm ra giá trị lớn nhất (nếu mảng cần xếp theo thứ tự tăng dần) hoặc ngược lại tìm giá trị nhỏ nhất (nếu mảng cần xếp theo th
Các bước: (ứng với sắp xếp mảng tăng dần)
+ Bước 1 : Xây dựng cấu trúc Heap cho các phần tử có nút con để t
ìm được phần tử lớn nhất a[i]>= a[2i +1] và a[i]>=a[2i+2]
(nếu có nút con phải) Từ vị trí index: n/2 –
1 trở về 0 là các nút có con nên quá trình sẽ bắt đầu bằng việc xây dựng cấu trúc Binary Heap từ a[n/2 -1] đến a[0]
Biểu diễn dưới dạng mã giả:
Xây dựng cấu trúc Binary Heap ở nút cha i:
void Heapify(int* a, int n, int i)
int max = left;
if (right<n && a[right]>a[max])
Trang 21+ Bước 2: Hoán đổi vị trí a[0] với a[i] và thực hiện quá trình Heap
ify nhưng không xét a[i], sau đó tiếp tục giảm i:
Hình minh họa một ví dụ của Heap sort:
Trang 24Nguồn ảnh: Heap Sort Image - Imgur
Độ phức tạp thời gian: O(nlogn)
(trong đó: Heapify O(logn), vòng lặp for kết hợp với Heapify =
Conquer) bằng việc chia mảng nhiều lần để tạo thành các mảng co
n rồi gộp lại theo thứ tự cho đến khi đã thống nhất các mảng con thành 1 mảng
Thuật toán Chia để trị (Divide and Conquer
Strategy):
tính, chia để trị là một mô hình thiết kế thuật toán quan trọng dựa t
rên đệ quy với nhiều phân nhánh Thuật toán chia để trị hoạt động bằng cách chia bài toán thành nhiều bài toán nhỏ hơn thuộc cùng th
ể loại, cứ như vậy lặp lại nhiều lần, cho đến khi bài toán thu được đ
ủ đơn giản để có thể giải quyết trực tiếp
Sau đó lời giải của các bài toán nhỏ được tổng hợp lại thành lời giả
i cho bài toán ban đầu
Thuật toán Merge sort lặp đi lặp lại quá trình chia mảng thành 2 mảng con và tiếp tục chia cho tới khi các mảng con chỉ chứa 1 phần tử Sau đó gộp 2 mảng con nhỏ thành mảng đã sắp xếp và tiếp tục gộp mảng tạo thành với 1 mảng tạo thành khác Việc gộp nửa mảng kết thúc khi ta chỉ còn 1 mảng duy nhất đã sắp xếp Các quá trình chia mảng và gộp mảng được thực hiện bằng Đệ quy
Minh họa thuật toán Merge sort theo hình vẽ:
Trang 25Nguồn ảnh: programiz.com
Các bước:
Thuật toán Merge sort chủ yếu dựa trên 2 quá trình là chia mảng rathành các mảng con và gộp các mảng con lại một cách có thứ tự
+ Quá trình 1: Chia mảng thành các mảng con
Lặp đi lặp lại việc chia mảng thành 2 mảng con cho đến khi tới giaiđoạn các mảng con chỉ có 1 phần tử
void mergeSort(int* arr, int left, int right) {
if (left < right) {
Trang 26int mid = (left + right) / 2;
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}
}
Theo mã giả trên, mergeSort là hàm chia mảng được truyền vào ban đầu left = 0 và right = n-1 Sau khi việc phân chia kết thúc, quátrình 2 (hàm merge) sẽ tiếp tục
+ Quá trình 2: Kết hợp các mảng con với nhau một cách có thứ tự
cho đến khi có được 1 mảng hoàn chỉnh
Hàm merge vận hành như sau:
1 Tạo ra mảng leftArray và rightArray copy từ mảng ban đầu Truyền vào cho hàm merge các mảngleft, mid và right để đánh dấu index của mảng copy Mảng leftArray sẽ copy từ index left đến index mid của mảng arr Mảng rightArray copy từ index mid +
1 đến right của mảng Arr
void merge(int* arr, int left, int mid, int right) {
int nLeft = mid - left + 1;
int nRight = right - mid;
int* leftArray = new int[nLeft];
int* rightArray = new int[nRight];
for (int i = 0; i < nLeft; i++) {
leftArray[i] = arr[left + i];
} for (int j = 0; j < nRight; j++) {
Trang 27if (leftArray[i] <= rightArray[j]) {
arr[k] = leftArray[i];
i++;
} else {
arr[k] = rightArray[j];
j++;
} k++;
}
3 Đặt các phần tử còn sót lại sau khi kết thúc quá trình so sánh và đặt:
while (i < nLeft) { arr[k] = leftArray[i];
i++;
k++;
} while (j < nRight) { arr[k] = rightArray[j];
Độ phức tạp thời gian: O(nlogn)
Độ phức tạp không gian: O(n)
8-QuickSort:
Ý tưởng: Quick
sort là một thuật toán sắp xếp chia để trị (Divide and Conquer
Trang 28algorithm) bằng cách chọn một phần tử trong mảng làm pivot (điểm đánh dấu) và chia mảng ra thành hai mảng con dựa vào pivot đã chọn Quá trình được lặp đi lặp lại với các mảng con
Mô tả: Chọn một phần tử trong mảng làm pivot
(điểm đánh dấu) và chia mảng đã cho thành hai mảng con dựa v
ào pivot đó (mảng các số lớn hơn pivot và mảng các số nhỏ hơn pivot) Có một số cách chọn pivot:
left và right là hai phần tử thuộc rìa bên trái và bên phải của mảng
Bước 2: Nếu left >= right -> Tiến đến bước 7
Trang 29Bước 3 : Chọn pivot: chọn giá trị phần tử ở giữa mảng làm pivot C
ho hai con chạy i, j xuất phát từ left và right
Bước 4: Cho i tăng dần tới khi a[i]>= pivot Cho
j giảm dần tới khi a[j]<=pivot
Bước 5: Hoán vị a[i], a[j]
Sau đó tiếp tục tăng i lên 1 đơn vị và giảm j xuống 1 đơn vị
Bước 6: Nếu i<= j quay lại bước 4 Nếu i>j,
chia mảng thành hai phần với mảng con 1 có left là 0,
right là j; mảng con 2 có left là i, và right vẫn là right
Quay lại bước 2
Bước 7: Kết thúc thuật toán
Hình minh họa thuật toán Quick sort (Pivot là giá trị ở giữa)
(Nguồn: Chuong Le Hoang – WordPress.com)