正文
“找到第 K 大的元素”或“找到前 K 个高频元素”是堆(Heap/PriorityQueue)最经典的应用场景。利用堆的特性,可以将原本需要全排序的 $O(N \log N)$ 复杂度优化到 $O(N \log K)$。
算法模板 (找到第 K 大的元素)
1 | import java.util.PriorityQueue |
使用说明
核心逻辑:
- 求前 K 大:维护一个大小为 $K$ 的最小堆。当堆满时,新元素若大于堆顶,则弹出堆顶并插入新元素。最终堆中留下的就是最大的 $K$ 个数,堆顶是第 $K$ 大。
- 求前 K 小:维护一个大小为 $K$ 的最大堆(
PriorityQueue(Collections.reverseOrder()))。堆顶是这 $K$ 个最小值中最大的一个,即第 $K$ 小。
核心优势:
- 空间效率:不需要加载全部数据,只需维持 $K$ 个元素的堆。
- 时间效率:$O(N \log K)$。当 $K \ll N$ 时,性能接近 $O(N)$。
常见变形:
- 前 K 个高频元素:配合哈希表记录频率,堆中存储
Map.Entry并根据频率排序。 - 合并 K 个有序链表:利用最小堆维护 $K$ 个链表的当前头节点。
- 数据流中的第 K 大:保持堆的大小为 $K$,持续
offer和poll。
- 前 K 个高频元素:配合哈希表记录频率,堆中存储
注意点:
- 在 Kotlin/Java 中,
PriorityQueue默认是最小堆(队首是最小值)。 - 如果需要最大堆,需传入比较器:
PriorityQueue<Int> { a, b -> b - a }。 - 对于海量数据处理,堆是内存占用最友好的方案。
- 在 Kotlin/Java 中,