Ổn định & so sánh các thuật toán sắp xếp
Bạn đã có trong tay sáu thuật toán sắp xếp. Bài này tổng kết: hiểu rõ tính ổn định (stable) — một tính chất tinh tế nhưng cực kỳ quan trọng trong thực tế — và có một bảng so sánh đầy đủ để chọn đúng thuật toán cho từng tình huống.
1. Trực quan: ổn định nghĩa là "không xáo trộn người ngang điểm"
Tưởng tượng một bảng xếp hạng học sinh đang sắp theo tên (A, B, C...). Giờ sếp bảo: "Sắp lại theo điểm số". Có hai bạn cùng 8 điểm: An (đứng trước) và Bình (đứng sau).
- Thuật toán ổn định (stable): sau khi sắp theo điểm, An vẫn đứng trước Bình. Thứ tự cũ giữa hai người ngang điểm được giữ nguyên.
- Thuật toán không ổn định (unstable): có thể Bình bị nhảy lên trước An. Hai người ngang điểm bị xáo trộn so với thứ tự ban đầu.
Goc (da sap theo TEN): An:8 Binh:8 Cuong:7
Sap theo DIEM giam dan:
On dinh -> An:8 Binh:8 Cuong:7 (An van truoc Binh)
Khong on dinh -> Binh:8 An:8 Cuong:7 (bi dao!)
Tính ổn định quan trọng khi ta sắp nhiều tầng: sắp theo tên trước, rồi sắp theo điểm. Chỉ thuật toán ổn định mới giữ được "trong cùng điểm thì theo tên".
2. Vì sao ổn định lại quan trọng trong thực tế
Bạn hiếm khi sắp một danh sách số đơn thuần. Thực tế là sắp các bản ghi nhiều trường: đơn hàng có (ngày, khách, giá), nhân viên có (phòng, tên, lương)... Khi đó:
- Sắp đa tiêu chí bằng cách sắp lần lượt từ tiêu chí phụ tới tiêu chí chính, dựa vào tính ổn định để các tầng trước không bị phá.
- Kết quả dễ đoán, lặp lại được — quan trọng cho kiểm thử và trải nghiệm người dùng (bảng không "nhảy lung tung" mỗi lần sắp lại).
3. Chạy debug: thấy tính ổn định "sống"
Sắp các cặp (chu, so) theo so. Hai cặp cùng số là ("a", 2) và ("c", 2) — thuật toán ổn định
giữ a trước c.
4. Kiểm chứng bằng code
5. Bảng so sánh đầy đủ
| Thuật toán | Tốt nhất | Trung bình | Xấu nhất | Bộ nhớ | Ổn định | Khi nào dùng |
|---|---|---|---|---|---|---|
| Bubble | O(n) | O(n^2) | O(n^2) | O(1) | Có | Dạy học; mảng tí hon |
| Selection | O(n^2) | O(n^2) | O(n^2) | O(1) | Không | Khi số phép đổi chỗ phải ít nhất |
| Insertion | O(n) | O(n^2) | O(n^2) | O(1) | Có | Mảng nhỏ hoặc gần như đã sắp |
| Merge | O(n log n) | O(n log n) | O(n log n) | O(n) | Có | Cần ổn định + đảm bảo O(n log n); linked list |
| Quick | O(n log n) | O(n log n) | O(n^2) | O(log n) | Không | Nhanh thực tế; tiết kiệm bộ nhớ |
| Heap | O(n log n) | O(n log n) | O(n log n) | O(1) | Không | Đảm bảo O(n log n) + O(1) bộ nhớ |
| Counting | O(n + k) | O(n + k) | O(n + k) | O(n + k) | Có | Khóa nguyên, dải k nhỏ |
| Radix | O(d(n + k)) | O(d(n + k)) | O(d(n + k)) | O(n + k) | Có | Số nguyên/chuỗi độ dài cố định |
(Hai dòng cuối — Counting/Radix — là sắp xếp phi so sánh, học kỹ ở bài 3.1.6).