堆详解(最大堆、最小堆、优先队列)
二叉堆是一棵完全二叉树,最大堆要求父节点不小于孩子,最小堆相反。它只保证局部偏序,并不是整体有序。完全树可紧凑地放在数组中:零下标时,节点 i 的孩子为 2i+1 和 2i+2。
插入先把新元素放到数组末尾,再与父节点比较并上浮;删除堆顶时,用末尾元素覆盖根,缩短数组,再与更合适的孩子交换并下沉。树高为 O(log n),因此插入和删除顶都是 O(log n),读取堆顶为 O(1)。
从无序数组建堆不必逐个插入。可从最后一个非叶节点开始向前下沉,虽然单次下沉最高 O(log n),但大量底层节点移动很短,合计为 O(n)。堆排序建最大堆后反复把根换到末尾并缩小堆区,时间 O(n log n)、额外空间 O(1),但通常不稳定。
优先队列是抽象接口,堆是常用实现。Top K、任务调度、多路归并和 Dijkstra 都可使用它。若需更新任意元素优先级,普通堆还需要位置索引,或采取重复入堆并在弹出时丢弃旧记录。
误区是认为遍历堆会得到有序序列、混淆最大 K 与堆方向、把建堆写成 O(n log n)、修改比较器依赖的字段后不重建,以及忘记空堆检查。小结:堆擅长反复取得一个极值,不擅长搜索任意值或直接输出完整顺序。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Dai Wei!
评论

