Menu
CoddyTech

Sắp xếp vun đống (heap sort)

Cập nhật lần cuối

Sắp xếp vun đống (heap sort) coi mảng như một binary heap. Đầu tiên, thuật toán dựng một max-heap để phần tử lớn nhất nằm ở gốc (chỉ số 0). Sau đó nó liên tục đổi chỗ gốc với phần tử cuối cùng chưa được sắp xếp, cố định giá trị lớn nhất vào đúng chỗ, rồi đẩy gốc mới xuống (sift down) để khôi phục tính chất heap. Nhấn Phát ở trên để xem quá trình dựng heap và các lần lấy phần tử ra.

Heap sort bảo đảm thời gian O(n log n) như merge sort, nhưng sắp xếp tại chỗ với chỉ O(1) bộ nhớ phụ. Thuật toán không ổn định và thường tận dụng bộ nhớ đệm kém hơn quicksort, nên hay được chọn khi cần cả cận thời gian được bảo đảm lẫn bộ nhớ cố định.

Độ phức tạp thời gian và không gian

Trường hợpĐộ phức tạpGhi chú
Tốt nhấtO(n log n)Dựng heap + n lần lấy ra
Trung bìnhO(n log n)Thứ tự ngẫu nhiên
Xấu nhấtO(n log n)Được bảo đảm
Không gianO(1)Sắp xếp tại chỗ
Ổn địnhKhôngSift down làm đảo thứ tự các phần tử bằng nhau

Từng bước thực hiện

BướcĐiều gì xảy ra
1Dựng max-heap từ mảng (sift down bắt đầu từ nút cha cuối cùng).
2Đổi chỗ gốc (giá trị lớn nhất) với phần tử cuối cùng của heap.
3Thu nhỏ heap đi một phần tử: ô cuối đó giờ đã được sắp xếp.
4Sift down gốc mới để khôi phục tính chất max-heap.
5Lặp lại cho đến khi heap chỉ còn một phần tử.

Ví dụ minh họa

Sắp xếp [3, 1, 6, 5, 2, 4]. Dấu | đánh dấu ranh giới giữa heap đang thu nhỏ và phần đuôi đã sắp xếp:

LượtMảngThao tác
Dựng heap[6, 5, 4, 1, 2, 3]Sift down từ nút cha cuối cùng để dựng max-heap; 6 giờ nằm ở gốc.
1[5, 3, 4, 1, 2 | 6]Đổi gốc 6 với ô cuối, thu nhỏ heap và sift down 3.
2[4, 3, 2, 1 | 5, 6]Đổi gốc 5 ra, rồi sift down 2 để 4 lên gốc.
3[3, 1, 2 | 4, 5, 6]Đổi gốc 4 ra, rồi sift down 1 để 3 lên gốc.
4[2, 1 | 3, 4, 5, 6]Đổi gốc 3 ra; 2 đã thỏa mãn tính chất heap.
5[1 | 2, 3, 4, 5, 6]Đổi gốc 2 ra; chỉ còn một phần tử, nên mảng đã được sắp xếp.

Khi nào nên dùng heap sort

Nên dùng khiTránh dùng khi
Bạn cần bảo đảm trường hợp xấu nhất O(n log n), không có rủi ro O(n²).Bạn cần một thuật toán sắp xếp ổn định, giữ nguyên thứ tự các khóa bằng nhau.
Bộ nhớ eo hẹp: thuật toán sắp xếp tại chỗ với chỉ O(1) bộ nhớ phụ.Hiệu năng bộ nhớ đệm quan trọng và dữ liệu vừa trong bộ nhớ: quicksort thường nhanh hơn.
Bạn vốn đang duy trì một heap (ví dụ hàng đợi ưu tiên) trên dữ liệu.Bạn muốn ít phép so sánh nhất: merge sort và quicksort thường so sánh ít hơn trong thực tế.
Dữ liệu đầu vào không đáng tin có thể kích hoạt trường hợp xấu nhất của quicksort và bạn không thể ngẫu nhiên hóa.Dữ liệu gần như đã sắp xếp: sắp xếp chèn chạy trong thời gian gần tuyến tính với loại dữ liệu này.

Code Heap Sort

Một cách cài đặt Heap Sort gọn gàng, chạy được ngay bằng Python, JavaScript, Java, C++, C. Chọn một ngôn ngữ, sao chép code hoặc mở sẵn trong Coddy Playground.

Code Heap Sort bằng Python

Python
1def heap_sort(a):2    n = len(a)3    # Build a max-heap, deepest parent first4    for i in range(n // 2 - 1, -1, -1):5        sift_down(a, i, n)6    # Repeatedly move the max to the end and shrink the heap7    for end in range(n - 1, 0, -1):8        a[0], a[end] = a[end], a[0]9        sift_down(a, 0, end)10    return a11
12
13def sift_down(a, i, size):14    while True:15        largest = i16        left, right = 2 * i + 1, 2 * i + 217        if left < size and a[left] > a[largest]:18            largest = left19        if right < size and a[right] > a[largest]:20            largest = right21        if largest == i:22            return23        a[i], a[largest] = a[largest], a[i]24        i = largest25
26
27nums = [12, 11, 13, 5, 6, 7]28print("Before:", nums)29heap_sort(nums)30print("After: ", nums)
Chạy code này trong Playground Python

Câu hỏi thường gặp về heap sort

Độ phức tạp thời gian của heap sort là bao nhiêu?
Heap sort là O(n log n) trong trường hợp tốt nhất, trung bình và xấu nhất. Dựng heap tốn O(n) và mỗi lần trong n lần lấy ra tốn O(log n). Thuật toán dùng thêm O(1) bộ nhớ.
Heap sort có ổn định không?

Không. Thao tác sift down có thể đưa các phần tử bằng nhau vượt qua nhau, nên heap sort không giữ nguyên thứ tự tương đối của các khóa bằng nhau.

Khi nào nên dùng heap sort?
Dùng heap sort khi bạn cần bảo đảm trường hợp xấu nhất O(n log n) mà chỉ tốn thêm O(1) bộ nhớ. Thuật toán tránh được rủi ro O(n²) của quicksort mà không cần bộ đệm O(n) như merge sort, với cái giá là mất tính ổn định và hiệu năng bộ nhớ đệm kém hơn.
Heap sort và quicksort khác nhau như thế nào?
Cả hai đều sắp xếp tại chỗ, nhưng quicksort có trường hợp xấu nhất O(n²) còn heap sort bảo đảm O(n log n). Trong thực tế quicksort thường nhanh hơn nhờ tính cục bộ bộ nhớ đệm tốt hơn và ít lần đổi chỗ hơn, nên heap sort chủ yếu được ưu tiên khi bắt buộc phải bảo đảm cận trong trường hợp xấu nhất.
Heap sort liên quan thế nào đến hàng đợi ưu tiên?

Binary heap là cách cài đặt chuẩn của hàng đợi ưu tiên, và heap sort về bản chất là liên tục lấy phần tử lớn nhất ra khỏi hàng đợi đó. Nếu dữ liệu của bạn vốn đã nằm trong một heap, việc lấy ra từng phần tử sẽ tự động cho bạn thứ tự đã sắp xếp.

Heap sort cần max-heap hay min-heap?

Để sắp xếp tăng dần tại chỗ, hãy dùng max-heap: phần tử lớn nhất được đổi về cuối sau mỗi lượt, làm phần đuôi đã sắp xếp lớn dần từ bên phải. Min-heap sẽ cho thứ tự giảm dần nếu sắp xếp tại chỗ, hoặc tăng dần nếu bạn lấy các phần tử ra một mảng riêng.

Hình minh họa các ngôn ngữ lập trình của Coddy

Thành thạo thuật toán cùng Coddy

Bắt đầu