Luke a Pro

Luke Sun

Developer & Marketer

🇺🇦
EN||

堆排序 (HeapSort)

| , 2 minutes reading.

堆排序:生存竞赛

算法背后的故事:王权的逻辑

想象一个王国,国王必须是全国最强壮的人。当现任国王去世时,全国会举行一场比武大赛。所有的公爵、骑士和士兵都在一个层级森严的对阵表中竞争。胜者成为新任国王。

但这里有一个规则:一旦胜者加冕并被载入史册,他就必须离开王国。现在,剩下的竞争者必须重新组织层级,寻找下一位最强领袖。

这就是 堆排序 (HeapSort) 的精髓,由 J. W. J. Williams 在 1964 年发明。它不依赖于像快速排序那样的“划分世界”,也不依赖于像归并排序那样的“合并世界”。它依赖的是一套竞争性的层级制度(即“堆”)。

为什么需要它?

堆排序是高效排序算法中的“极简主义者”。

  • 原地排序: 与归并排序不同,它不需要任何额外内存 (O(1)O(1) 空间)。
  • 稳定性: 这里的稳定性指性能。和归并排序一样,它的速度始终是 O(NlogN)O(N \log N),永远不会像快速排序那样遇到“倒霉的一天”。
  • 提取 TopK: 如果你只需要 100 万件商品中价格最高的 100 件,你不需要排序全部。你只需要运行 100 次“比武大赛”即可。

算法是如何“思考”的

这个算法像是一个严苛的比赛组织者,负责维护一套严格的层级。

  1. 海选 (建堆): 它拿出一份随机列表,强行将其塞入一个“大顶堆”结构中。在这个树形结构里,每个父亲都必须比他的孩子更强大(数值更大)。此时,最强的元素就在最顶端。

  2. 胜出 (提取): 顶端的元素是获胜者。它被移到列表的末尾(它的终身归宿)。

  3. 重组 (堆化): 现在顶端空了。组织者从树的最底层随手抓起一个卑微的“平民”放在顶端。 可以预见,这个平民很弱。因此,他必须在层级中“下沉”,不断与比他更强壮的孩子交换位置,直到层级秩序重新恢复。

  4. 循环: 比赛继续进行,直到只剩下最后一个人。

工程决策:优先队列

在生产环境中,堆的排序版本其实不如它的孪生兄弟——优先队列 (Priority Queue) 常用。

  • 操作系统: CPU 下一步该处理哪个任务?当然是优先级最高的那个。
  • 流式数据: 实时寻找“前 10 名热门话题”。堆能保持前 10 名在顶端,让你高效地无视剩下的数百万个“落选者”。

实现参考 (Python)

import heapq

def heapsort(arr):
    # Python 的 heapq 实现的是小顶堆 (Min-Heap)。
    # 如果我们要升序排列,可以直接利用它。
    h = []
    for value in arr:
        heapq.heappush(h, value)
    
    # 依次弹出最小值,即可得到有序列表
    return [heapq.heappop(h) for _ in range(len(h))]

# 原地逻辑示例 (手动堆化)
def heapify(arr, n, i):
    largest = i
    left = 2 * i + 1
    right = 2 * i + 2

    # 如果左孩子比父亲大
    if left < n and arr[i] < arr[left]:
        largest = left
    # 如果右孩子比目前最大的还大
    if right < n and arr[largest] < arr[right]:
        largest = right

    # 如果最大的不是父亲,交换并继续向下调整
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr, n, largest)

小结

堆排序教会我们:秩序源于竞争。通过建立层级并系统性地选拔最优者,我们可以在不占用额外空间的情况下达成完美的秩序。它提醒我们,即使面对数十亿数据,有时我们也只需要知道“此刻谁是最好的”。