Quick Sort

Thuật toán sắp xếp nhanh nhất trong thực tế — chọn một pivot, phân vùng mảng, rồi đệ quy sắp xếp hai nửa. Trung bình O(n log n).

Tốc độ
Chưa xử lý Pivot Con trỏ i Con trỏ j Hoán đổi Đã sắp xong
So sánh0
Hoán đổi0
Gọi đệ quy0
Nhấn Chạy hoặc Bước kế...

Ý tưởng — Partition

Quick Sort hoạt động theo nguyên tắc Divide & Conquer (chia để trị):

  1. Chọn pivot: chọn một phần tử làm "chốt" (thường là phần tử cuối)
  2. Partition: sắp xếp lại mảng sao cho tất cả phần tử < pivot nằm bên trái, > pivot nằm bên phải
  3. Đệ quy: áp dụng Quick Sort cho nửa trái và nửa phải
💡
Tại sao nhanh? Quick Sort tận dụng tốt cache CPU (locality of reference) và có hệ số nhân nhỏ hơn Merge Sort. Trung bình chỉ cần ~1.39n log n so sánh — nhanh hơn đáng kể trong thực tế dù cùng O(n log n).

Cài đặt C — Lomuto Partition

quick_sort.c
#include <stdio.h>

void swap(int *a, int *b) {
    int t = *a; *a = *b; *b = t;
}

/* Lomuto partition: pivot = a[hi]
   Trả về chỉ số cuối cùng của pivot */
int partition(int a[], int lo, int hi) {
    int pivot = a[hi];
    int i     = lo - 1; /* ranh giới vùng nhỏ hơn pivot */

    for (int j = lo; j < hi; j++) {
        if (a[j] <= pivot) {
            i++;
            swap(&a[i], &a[j]);
        }
    }
    swap(&a[i + 1], &a[hi]); /* đặt pivot vào đúng vị trí */
    return i + 1;
}

void quickSort(int a[], int lo, int hi) {
    if (lo < hi) {
        int p = partition(a, lo, hi);
        quickSort(a, lo,     p - 1); /* nửa trái  */
        quickSort(a, p + 1, hi    ); /* nửa phải */
    }
}

int main() {
    int a[] = {10, 80, 30, 90, 40, 50, 70};
    int n = sizeof(a) / sizeof(a[0]);
    quickSort(a, 0, n - 1);
    for (int i = 0; i < n; i++) printf("%d ", a[i]);
    /* Output: 10 30 40 50 70 80 90 */
    return 0;
}

Cài đặt C — Hoare Partition (hiệu quả hơn)

quick_sort_hoare.c
/* Hoare partition: ít hoán đổi hơn Lomuto ~3x */
int hoarePartition(int a[], int lo, int hi) {
    int pivot = a[lo + (hi - lo) / 2]; /* pivot giữa */
    int i = lo - 1, j = hi + 1;

    while (true) {
        do { i++; } while (a[i] < pivot);
        do { j--; } while (a[j] > pivot);
        if (i >= j) return j;
        swap(&a[i], &a[j]);
    }
}

Độ phức tạp

Trường hợpThời gianKhông gianGhi chú
Tốt nhấtO(n log n)O(log n)Pivot luôn chia đôi mảng
Trung bìnhO(n log n)O(log n)~1.39n log n so sánh
Tệ nhấtO(n²)O(n)Mảng đã sắp + pivot = phần tử đầu/cuối
⚠️
Tránh worst case: Chọn pivot ngẫu nhiên (randomized pivot) hoặc dùng "median-of-3" (trung vị của 3 phần tử đầu/giữa/cuối) để tránh O(n²) trên mảng đã sắp.