Lý thuyếtThuật toán chọn Selection Sort sắp xếp một mảng bằng cách đi tìm phần tử cógiá trị nhỏ nhất giả sử với sắp xếp mảng tăng dần trong dãy số chưa được sắp xếp vàđổi cho phần tử nhỏ
GIẢI THUẬT SẮP XẾP CHỌN (SELECTION SORT)
Thuật toán chọn (Selection Sort) sắp xếp một mảng bằng cách đi tìm phần tử có giá trị nhỏ nhất (giả sử với sắp xếp mảng tăng dần) trong dãy số chưa được sắp xếp và đổi cho phần tử nhỏ nhất đó với phần tử ở đầu dãy chưa được sắp xếp (không phải đầu mảng) Thuật toán sẽ chia mảng làm 2 mảng con:
- Mảng con đã được sắp xếp
- Mảng con chưa được sắp xếp
Tại mỗi bước lặp của thuật toán, phần tử nhỏ nhất ở mảng con chưa được sắp xếp sẽ được di chuyển về đoạn đã sắp xếp.
Giả sử cần sắp xếp giảm dần một danh sách có n phần từ a0, a1, a2, …, an-1
Chọn phần tử có giá trị lớn nhất trong danh sách ban đầu Hoán đổi vị trí giữa phần tử lớn nhất với phần tử đầu tiên trong danh sách Sau khi đổi vị trí, phần tử tại vị trí đầu tiên sẽ có giá trị lớn nhất trong danh sách.
Sau đó, xem danh sách cần sắp xếp hiện tại chỉ gồm n-1 phần tử, bắt đầu từ phần tử thứ 2 trong danh sách ban đầu, tức là danh sách hiện tại chỉ gồm a1, a2, …, an-1. Lặp lại quá trình trên cho danh sách hiện tại đến khi danh sách hiện tại chỉ còn 1 phần tử.
3 Các bước thực hiện bài toán
- Bước 2: tìm phần tử a[max] lớn nhất trong dãy hiện hành từ a[i] đến a[n-1].
- Bước 3: đổi chỗ a[max] và a[i]
- Bước 4: nếu i < n-1 thì gán i = i+1, rồi lặp lại bước 2, ngược lại -> dừng
- Cho dãy số ban đầu:
- Thực hiện tìm số lớn nhất trong dãy và cho lên vị trí đầu tiên của dãy Đổi chỗ 56 lên đầu dãy
- Thực hiện kiểm tra tiếp dãy số từ vị trí thứ 2 tới cuối dãy Số lớn nhất là 34 nên giữ nguyên vị trí các số:
- Tiếp tục kiếm tra từ vị trí thứ 3 và thấy số lớn nhất là 23 nên vẫn giữ nguyên vị trí.
- Kiểm tra tiếp từ số ở vị trí thứ 4 của dãy, thấy 22 lớn hơn 12 nên đảo 22 lên vị trí thứ 4, và ta được dãy sắp xếp giảm dần hoàn chỉnh:
5 Thời gian tính toán và độ phức tạp
- Số lượng phép so sánh: (n-1) + (n-2) + (n-3) + … + 1 = n.(n-1) ~ n 2
- Ngoài ra, chúng ta có thể phân tích độ phức tạp bằng cách quan sát số vòng lặp có
2 vòng lặp nên độ91 phức tạp là n.n = n 2
- Không gian sử dụng bộ nhớ: O(1).
6 Ưu điểm và nhược điểm của thuật toán a Ưu điểm:
- Thuật toán đơn giản, dễ thực hiện.
- Số lần đổi vị trí các số trong dãy ít. b Nhược điểm:
- Chỉ được áp dụng trong các trường hợp có số lượng phần tử cần so sánh ít.
- Không nhận biết được mảng đã được sắp xếp.
7 CODE a Hàm đổi chỗ 2 số nguyên: b Hàm Selection Sort: c Hàm xuất mảng: d Hàm main: e Kết quả:
- Selection Sort không phù hợp với các dữ liệu lớn do công đoạn tìm phần tử nhỏ nhất trong nhóm “lộn xộn” trông tưởng chừng đơn giản (nếu nhìn bằng mắt thường thì khá nhanh để phát hiện ra), tuy nhiên khi thực sự bắt tay vào thực hiện thì chúng ta sẽ gặp khá nhiều khó khăn liên quan đến thời gian.
- Thứ tự sắp xếp của Selection Sort cũng được đánh giá là chưa ổn định.
- Chúng ta cần vận dụng linh hoạt và khéo léo vào tùy trường hợp khác nhau.
GIẢI THUẬT SẮP XẾP CHÈN (INSERT SORT)
1 Lý thuyết giải thuật sắp xếp chèn
Sắp xếp chèn (Insert Sort) là một thuật toán sắp xếp đơn giản hoạt động tương tự như cách bạn sắp xếp các thẻ chơi trong tay Mảng hầu như được chia thành một phần được sắp xếp và một phần chưa được sắp xếp Các giá trị từ phần chưa được sắp xếp được chọn và đặt ở vị trí chính xác trong phần được sắp xếp. Đặc điểm của Sắp xếp Chèn: Thuật toán này là một trong những thuật toán đơn giản nhất với cách thực hiện đơn giản
Về cơ bản, sắp xếp chèn hiệu quả đối với các giá trị dữ liệu nhỏ Sắp xếp chèn có bản chất thích ứng, tức là nó thích hợp cho các tập dữ liệu đã được sắp xếp một phần.
Thuật toán sắp xếp chèn (Insert Sort Algorithm) Để sắp xếp một mảng có kích thước n theo thứ tự giảm dần:
Bước 1 : Lặp lại từ arr [1] đến arr [n] trên mảng.
Bước 2 : So sánh phần tử hiện tại với phần tử trước của nó.
Bước 3 : Nếu phần tử chính lớn hơn phần tử trước của nó, hãy so sánh nó với các phần tử trước đó Di chuyển các phần tử nhỏ hơn lên một vị trí để tạo khoảng trống cho phần tử được hoán đổi. Ảnh minh họa thuật toán sắp xếp chèn
Cách hoạt động của Insert Sort
Ví dụ: Sắp xếp mảng a = [5, 6, 11, 12, 13] thành dãy giảm dần: Đầu tiên ta có mảng a = [5, 6, 11, 12, 13]
Chúng ta sẽ bắt đầu lặp từ phần tử thứ hai của mảng là a[i] với i = 1) tới phần tử cuối cùng của mảng là a[i] với i = 4).
Tại i = 1 Lúc này key là phần tử thứ hai của mảng, key = a[1] = 6 lớn hơn
5 nên ta di chuyển 6 và chèn 6 vào trước 5 ( đổi chỗ a[0] và a[1] ).
Lúc này ta có mảng a = [6, 5, 11, 12, 13]
Tại i = 2, key = a[2] = 11 So sánh key với a[1], thấy 11 lớn hơn 5 nên a[2] và a[1] đổi chỗ Tiếp tục so sánh key với a[0] và a[1] = 11, vì 11 lớn hơn 6 nên a[0] và a[1] tiếp tục đổi chỗ.
Tại i = 3 Ta có key = a[3] = 12 lớn hơn a[2], a[1], a[0] tương ứng 11, 6, 5 nên sẽ di chuyển về đầu, tức a[0] = key = 12 Chỉ số của các phần tử a[i] hiện tại sẽ thành a[i+1].
Lúc này ta có mảng a = [12, 11, 6, 5, 13]
Tại i = 4 Ta có key = a[4] = 13 sẽ chuyển đến vị trí đầu tiên của mảng vì a[4] = 13 lớn hơn tất cả các giá trị trước nó, lúc này a[0] = key = 13 Các chỉ số của phần tử trong mảng tiếp tục tăng thêm 1 (a[i] = a[i+1]).
Kết thúc thuật toán, ta thu được mảng a đã sắp xếp giảm dần là a = [13, 12,
2 Code mẫu giải thuật Insert Sort
#include using namespace std; void insertionSort(int arr[], int n)
{ key = arr[i]; j = i - 1; while (j >= 0 && arr[j] < key)
} void printArray(int arr[], int n)
{ int i; for (i = 0; i < n; i++) cout = 0; i ) { swap(&arr[0], &arr[i]);
//Tạo cấu trúc heap cho phần tử gốc để lấy ra phần tử bé nhất heapify(arr, i, 0);
#include void swap(int *a, int *b) { int c = *a;
} void heapify(int arr[], int n, int i) { int largest = i; int left = 2 * i + 1; int right = 2 * i + 2; if (left < n && arr[left] > arr[largest]) largest = left; if (right < n && arr[right] < arr[largest]) largest = right; if (largest != i) { swap(&arr[i], &arr[largest]); heapify(arr, n, largest);
} void sort_heap(int arr[], int n) { for (int i = n / 2 - 1; i >= 0; i ) heapify(arr, n, i); for (int i = n - 1; i >= 0; i ) { swap(&arr[0], &arr[i]); heapify(arr, i, 0);
} void print(int arr[], int n) { for (int i = 0; i < n; ++i) printf("%d ", arr[i]); printf("\n");
} int main() { int n = sizeof(arr) / sizeof(arr[0]); sort_heap(arr, n); printf("Mảng sau khi sắp xếp là: \n"); print(arr, n);
Mảng sau khi sắp xếp là:
7 Độ phức tạp của thuật toán sắp xếp vun đống
Heap Sort có độ phức tạp về thời gian là O(nlogn) cho tất cả các trường hợp (trường hợp tốt nhất, trường hợp trung bình và trường hợp xấu nhất).
Chiều cao của một cây nhị phân hoàn chỉnh chứa n phần tử là logn
Như chúng ta đã thấy trước đó, để tạo cấu trúc Heap cho một phần tử có các cây con đã là Max Heap, chúng ta cần tiếp tục so sánh phần tử với các phần tử con bên trái và bên phải của nó và đẩy nó xuống dưới cho đến khi nó đạt đến điểm mà cả hai cây con của nó đều nhỏ hơn nó.
Trong trường hợp xấu nhất, chúng ta cần phải di chuyển một phần tử từ nút gốc đến nút lá để thực hiện nhiều phép so sánh và hoán đổi log(n) lần Điều này khiến thuật toán có độ phức tạp thời gian là O(n log(n)).
Trong giai đoạn xây dựng cấu trúc Max Heap, chúng ta đã thực hiện điều đó cho n2 phần tử nên độ phức tạp trong trường hợp xấu nhất của bước này là n2×log(n)≈nlog(n).
Trong bước sắp xếp, chúng ta hoán đổi phần tử gốc với phần tử cuối cùng và tạo cấu trúc Heap cho phần tử gốc Đối với mỗi phần tử, điều này lại làm tốn thời gian là log(n) vì chúng ta có thể phải đưa phần tử đó từ nút gốc đến nút lá Vì chúng ta lặp lại n lần này nên bước sắp xếp vun đống cũng là nlog(n).
Cũng vì các bước xây dựng cấu trúc Max Heap và sắp xếp vun đống được thực hiện lần lượt nên độ phức tạp của thuật toán không được nhân lên và nó vẫn theo thứ tự nlog(n).
Ngoài ra, nó thực hiện sắp xếp theo độ phức tạp về không gian là O(1) So với Sắp xếp nhanh, nó có trường hợp xấu nhất tốt hơn là (O(nlogn)) Sắp xếp nhanh có độ phức tạp O(n 2 ) cho trường hợp xấu nhất Nhưng trong các trường hợp khác, thuật toán sắp xếp nhanh hay QuickSort sẽ nhanh hơn.
8 Ứng dụng của thuật toán sắp xếp vun đống
Các hệ thống liên quan đến bảo mật và các hệ thống nhúng như nhân Linux sử dụng thuật toán sắp xếp vun đống vì giới hạn trên là O(nlogn) cho thời gian chạy của HeapSort và giới hạn trên không đổi là O(1) cho bộ nhớ phụ của nó.
Mặc dù HeapSort có độ phức tạp về thời gian là O(nlogn) ngay cả trong trường hợp xấu nhất, nó không có nhiều ứng dụng (so với các thuật toán sắp xếp khác nhưQuickSort, MergeSort) Tuy nhiên, cấu trúc dữ liệu cơ bản của nó, Heap, có thể được sử dụng hiệu quả nếu chúng ta muốn trích xuất phần nhỏ nhất (hoặc lớn nhất) từ danh sách