面试知识库

堆与 Top K 模板:前 K、动态最值、优先级问题的标准写法#

堆的本质就是:

始终把“当前最重要的元素”放在最前面。

当题目出现“前 K”“实时最值”“每次都要取当前最大/最小”“优先处理某个元素”时,堆通常就是最稳的选择。

⚡ 速记版模板#

PriorityQueue<Integer> minHeap = new PriorityQueue<>(); // 小顶堆:堆顶是当前堆中最小元素
for (int num : nums) { // 逐个处理数组元素
    if (minHeap.size() < k) { // 堆未满,先直接加入
        minHeap.offer(num);
    } else if (num > minHeap.peek()) { // 若当前元素比堆顶大,说明更有资格进入 TopK
        minHeap.poll(); // 先移除 TopK 中最小的那个
        minHeap.offer(num); // 再加入当前更大的元素
    }
}
return minHeap.peek(); // 维护完后,堆顶即第 k 大元素
java

🎯 什么时候想到堆?#

典型信号:

  • 第 K 大 / 第 K 小
  • 前 K 个高频元素
  • 数据流中的中位数
  • 合并多个有序结构
  • 每次需要快速取出当前最大值或最小值

Hot 100 里的典型题目:

💡 Java 里的堆怎么写?#

Java 用 PriorityQueue:

PriorityQueue<Integer> minHeap = new PriorityQueue<>(); // 小顶堆:peek() 始终是最小值
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((a, b) -> b - a); // 大顶堆:通过比较器反转顺序,peek() 是最大值
java
  • 默认是小顶堆
  • 想要大顶堆,就传比较器

🚀 模板一:固定大小为 K 的小顶堆#

适合:

  • 第 K 大元素
  • Top K 类问题

思路:

  • 堆里始终保留当前最大的 k 个元素
  • 堆顶就是这 k 个里最小的那个
  • 也就是第 k 大

🚀 模板二:按频率排序的堆#

适合:

  • 前 K 个高频元素

🚀 模板三:多路归并堆#

适合:

  • 合并 K 个有序链表
  • 合并 K 个有序数组

🚀 模板四:双堆结构#

适合:

  • 数据流中的中位数
  • 动态维护两半数据

⚠️ 易错点#

  1. 什么时候用小顶堆,什么时候用大顶堆

    • Top K 最大值:常用小顶堆
    • Top K 最小值:常用大顶堆
  2. 比较器别写反

    • 比较器决定堆顶是什么元素
  3. 不要忘记维护堆大小

    • 固定大小堆的关键就是及时弹出
  4. 链表或对象入堆时要明确比较字段

    • 不要只会写整数堆

🎨 面试时怎么说#

你可以这样说:

这题本质上是一个优先级维护问题,我用堆保证每次都能在 O(logn) 或 O(logk) 时间拿到当前最关键的元素。

📌 一句话总结#

堆最适合处理两类问题:

  • 你总想先拿到当前最大 / 最小元素
  • 你只关心前 K 名,不关心完整排序