tử 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:
Trang 1TRƯỜNG ĐẠI HỌC KHOA HỌC TỰ NHIÊN TP.HCM
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
Đánh giá của nhiệm
vụ vào tổng
nhiệm vụ thể bài làm
Selection Sort* (20), Flash Sort***
1 20127098 Đỗ Thụy Phương Vy
(20), Comparision-Experimental run (Table fill) (40), Command 1+2 (30)
Insertion Sort* (20), Shell Sort**
(20), Counting Sort*** (20), Comparision-
2 20127309 Nguyễn Lê Sơn Chart draw and comment (2 cái cuối)
(20), Run time-Experimental run (Table fill) (2 cái đầu) (20), Command
4 (15) Bubble Sort* (20), Shaker Sort* (20), Radix Sort*** (20), Comparision-
3 20127486 Dương Thanh Giang Chart draw and comment (2 cái đầu)
(20), Run time-Experimental run (Table fill) (2 cái cuối) (20), Command
3 (15) Merge Sort** (20), Quick Sort** (20),
4 20127653 Trần Nguyễn Lan Trinh Heap Sort** (20), Run time-Chart
draw and comment (40), Command 5
(15)
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à để
Trang 3tí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 4nhữ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
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 5Mô tả:
- Để sắp xếp mảng A có n phần tử, ta chọn lần lượt n−1 phần tử nhỏ nhất
- Lần lượt chọn n-1 phần thử nhỏ nhất bằng cách tại mỗi đoạn i+1 (0 ≤ I
- 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 6- 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 7tử 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 8Mã giả:
Trang 9Đá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 +Trung bình: O(n2)
Trang 10- 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ơn phầ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ét chỉ 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 11- 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 12Bướ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
Nếu a[j] > a[j-1]: a[j] ↔ a[j+1];
k = j; // lưu lại nơi xảy ra
Trang 13Ý 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 (InsertionSort) 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 14Mô 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áccủ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ếuchúng không theo thứ tự
- Phần tử thứ 0 được so sánh với phần tử thứ 4
Trang 15- 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ử đượclư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 16Sau 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 18-> 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 19- Việc gọi một ngăn xếp là tốn kém.
- 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 20Theo 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 21Để sắp xếp một mảng theo thứ tự, ta cần xây dựng cấu trúc BinaryHeap để tìm ra giá trị lớn nhất (nếu mảng cần xếp theo thứ tự tăngdần) hoặc ngược lại tìm giá trị nhỏ nhất (nếu mảng cần xếp theo th
ứ tự giảm dần)
Sau đó ta hoán đổi vị trí giá trị vừa tìm được đến cuối mảng, và
ch úng ta lại xây dựng cấu trúc Binary Heap
(nhưng lần này ta không xét phần tử đã được hoán đổi về cuối mảng) để tìm phần tử tiếp theo được hoán vị đến kế phần tử cuối
Quy trình cứ lặp đi lặp lại cho tới khi tất cả các phần tử đều đã đượ
c xét
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){
while (i <= n / 2 - 1){
int left = 2 * i + 1;
int right = 2 * i + 2; int max = left;
if (right<n && a[right]>a[max]){
max = right;
}
if (a[i] < a[max]){
swap(a[i], a[max]);
}
i = max;
}
Trang 22+ 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:
ví dụ của Heap sort:
Trang 25Nguồ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):
Theo Wikipedia, trong khoa học máy
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
Mô tả:
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 26Nguồ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 27int 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++)
{ rightArray[j] = arr[mid + 1 + j];
Trang 28if (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]; j++;
k++;
}
giá trị vào arr:
delete[] leftArray;
delete[] rightArray;
}
Độ 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 29algorithm) 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ơnpivot) Có một số cách chọn pivot:
đã được sắp xếp theo thứ tự
Các bước:
Bước 1: Cho
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 30Bướ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)