So với thuật toán Bubble sort, Quick Sort có tốc độ nhanh hơn. Thay vì đi theo sắp xếp từng cặp như Bubble sort, ta có thể chia dữ liệu ra thành 2 danh sách, rồi so sánh từng phần tử của danh sách với một phần tử được chọn (gọi là phần tử chốt hay pivot) và mục đích của chúng ta là đưa phần tử pivot về đúng vị trí của nó.
Kỹ thuật chọn pivot ảnh hưởng khá nhiều đến khả năng rơi vào các vòng lặp vô hạn đối với các trường hợp đặc biệt. Tốt nhất chọn pivot nằm ở trung vị của danh sách. Khi đó, sau log2(n) lần chia ta đạt được kích thước mảng con bằng 1.
Dưới đây là một số cách chọn pivot
Chọn phần tử đứng đầu hoặc đứng cuối làm pivot.
Chọn phần tử đứng giữa danh sách.
Chọn phần tử trung vị trong 3 phần tử đầu, đứng giữa và đứng cuối làm pivot:
Chọn phần tử ngẫu nhiên làm phần tử chốt. Tuy nhiên, cách này rất dễ dẫn đến khả năng rơi vào các trường hợp đặc biệt.
Bước 1: Chọn pivot
Bước 2: Khai báo 2 biến con trỏ để trỏ và duyệt 2 phía của pivot.
Bước 3: Biến bên trái trỏ đến từng phần tử mảng con bên trái và tương tự với bên phải.
Bước 4: Khi biến bên trái < pivot → di chuyển sang phải
Bước 5: Khi biến bên phải < pivot → di chuyển sang trái
Bước 6: Nếu không xảy ra trường hợp ở bước 4 và 5 → tráo đổi giá trị 2 biến trái và phải
Bước 7: Nếu trái > phải → đây là giá trị pivot mới.
(Xem rõ cách thức sắp xếp ở ảnh minh hoạ bên dưới hoặc mục 2, Giải thích)
Xét một dãy số như sau:
6, 1, 2, 7, 9, 3, 4, 5, 10, 8
Yêu cầu là sắp xếp dãy trên theo thứ tự không giảm từ trái qua phải.
Chọn pivot là 6, xét 2 "quân lính" là A và B lần lượt đặt ở 2 đầu của dãy số (A ở vị trí đầu bên trái và B ở cuối bên phải).
Luật hành quân như sau:
B đi trước, bắt đầu di chuyển về bên trái, đến khi gặp phần tử có giá trị nhỏ hơn giá trị của pivot thì dừng lại. Ở đây, B dừng ở vị trí số 5.
Đến lượt A, di chuyển về bên phải, đến khi gặp được phần tử có giá trị lớn hơn giá trị của pivot thì dừng lại. Ở đây, A dừng ở vị trí số 7.
Lúc này, ta đổi chỗ 2 số ở vị trí của A và B cho nhau, sau đó A và B trở về vị trí như lúc đầu, ta sẽ thu được dãy số sau:
6, 1, 2, 5, 9, 3, 4, 7, 10, 8
Tiếp tục cuộc hành quân như trên, lượt này ta sẽ cần đổi chỗ hai số 4 và 9 cho nhau, ta được dãy số:
6, 1, 2, 5, 4, 3, 9, 7, 10, 8
Đến với lượt hành quân tiếp theo, ta sẽ thấy b sẽ dừng lại ở vị trí của số 3. Tuy nhiên, A chưa tìm được số nào lớn hơn 6 đã "đụng mặt" với B.
Như vậy, ta coi lượt hành quân này là thất bại, và ta tiến hành đổi chỗ số 3 (số mà quân B đang dừng lại) với pivot là số 6. Ta thu được:
3, 1, 2, 5, 4, 6, 9, 7, 10, 8
Lúc này, chúng ta hãy xem pivot (số 6): sau loạt hành quân đầu tiên thì all những phần tử nằm bên trái pivot đều nhỏ hơn nó, và all những phần tử nằm bên phải đều lớn hơn nó.
Như vậy, ta đã đưa 6 về đúng vị trí của nó.
Tiếp theo, dãy được chia thành 2 dãy nhỏ hơn là dãy bên trái và bên phải số 6.
Ta tiếp tục thực hiện hành quân như trên đối với 2 dãy này và sẽ thu được thêm các pivot khác ở đúng vị trí và xuất hiện thêm các dãy con độ dài ngắn hơn.
Thực hiện đến cuối ta thu được dãy có thứ tự như mong muốn.
const quickSort = (arr) => { if(arr.length <= 1) { return arr; } let pivot = arr[0]; let leftArr = []; let rightArr = []; for(let i = 1; i < arr.length; i++) { if(arr[i] < pivot) { leftArr.push(arr[i]); } else { rightArr.push(arr[i]); } } return [...quickSort(leftArr), pivot, ...quickSort(rightArr)]; } const arr = [3, 1, 4, 2, 5]; console.log("Array: ", arr); const sortedArr = selectionSort(arr); console.log("Array after sorted: ", sortedArr);
Nhược điểm: