Xây một max-heap trên chính mảng (dạng cây nhị phân ẩn trong mảng), rồi lặp lại việc đưa gốc (phần tử lớn nhất) về cuối và sift-down lại phần còn lại. Luôn O(n log n), sắp xếp tại chỗ.
Một mảng có thể "giả vờ" là một cây nhị phân hoàn chỉnh: phần tử tại chỉ số i có con trái tại 2i+1, con phải tại 2i+2. Heap Sort tận dụng đúng mẹo đó, không cần con trỏ hay bộ nhớ phụ nào.
Sắp xếp một mảng số nguyên tăng dần, đảm bảo O(n log n) xấu nhất và sắp xếp tại chỗ (không như Merge Sort cần O(n) bộ nhớ phụ).
Bước 1 — Xây max-heap: biến mảng thành một max-heap (mọi nút cha ≥ hai nút con), bằng cách gọi
heapify (sift-down) từ nút cha cuối cùng ngược lên gốc.
Bước 2 — Rút phần tử lớn nhất: gốc heap (a[0]) luôn là phần tử lớn nhất. Đổi chỗ nó với phần tử
cuối mảng (đưa về đúng vị trí sắp xếp), thu nhỏ heap đi 1, rồi heapify lại gốc để khôi phục tính
chất heap. Lặp lại cho đến khi heap chỉ còn 1 phần tử.
| Trường hợp | Thời gian | Ghi chú |
|---|---|---|
| Mọi trường hợp | O(n log n) | Xây heap O(n), n lần rút phần tử mỗi lần O(log n) |
| Không gian | O(1) | Sắp xếp tại chỗ, không cần mảng phụ |
So với Merge Sort, Heap Sort không cần bộ nhớ phụ nhưng không ổn định (không giữ thứ tự tương đối của các phần tử bằng nhau) và trong thực tế thường chậm hơn Quick Sort do truy cập bộ nhớ kém liên tục hơn (nhảy giữa các chỉ số 2i+1, 2i+2).
Mảng [6, 2, 9, 4] (0-indexed, cha của i là (i-1)/2):
heapify từ nút n/2 - 1 = 1 ngược về 0 → được max-heap [9, 4, 6, 2].[2, 4, 6, 9], heapify phần [2, 4, 6] → [6, 4, 2, 9].[2, 4, 6, 9], heapify phần [2, 4] → [4, 2, 6, 9].[2, 4, 6, 9]. Kết quả cuối: [2, 4, 6, 9].priority_queue — bản thân STL priority_queue của C++ chính là một binary
heap cài trên mảng, giống hệt cấu trúc dùng trong Heap Sort.Kiểm tra nhanh
Trong mảng biểu diễn heap 0-indexed, con trái và con phải của nút i là gì?