LeetCode 703简单堆
数据流中的第 K 大元素 图解题解
这道题到底在问什么
实现 KthLargest 类: KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。 int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。
- KthLargest(3,[4,5,8,2])
- add(3), add(5), add(10)
- 输出
- 4,5,5
最优解:一步一步想明白
- 3下面按主解代码逐帧推进:先把「大小为 K 的最小堆」建起来,再看每次 add 如何用 O(log k) 维护「第 K 大」。
- 4不断 add,每次返回当前第 K 大这是一个数据流问题:数字不断加进来,每加一个就要立刻回答「目前第 K 大是多少」。每次都重新排序太慢,需要一个能动态维护的结构。
- 5堆里=最大的 K 个;堆顶(最小)=第 K 大诀窍:只留「目前最大的 K 个数」,用一个最小堆装它们。那么这 K 个里最小的(也就是堆顶),正好就是第 K 大。比它还小的数根本排不进前 K,不用管。
- 6求「第 K 大」反而用「最小堆」——留住最大的 K 个,看它们当中最小的那个。
- 7[4,5,8,2] → 留最大 3 个 [4,5,8]初始化:4、5、8、2 都进堆,但只能留 3 个,最小的 2 被弹掉。堆里剩 [4,5,8],堆顶 4 就是当前第 3 大。
- 83 < 堆顶 4 → 弹掉,堆顶不变add(3):先把 3 压进堆,超过 3 个了,弹掉最小的——正好就是刚进来的 3。堆顶还是 4,返回 4。比第 K 大还小的数,进来就被弹,毫无影响。
- 95 > 堆顶 4 → 弹掉 4,堆顶变 5add(5):5 压进堆,弹掉最小的 4。堆变成 [5,5,8],堆顶升到 5,返回 5。一个足够大的数进来,会把旧的最小挤出去、抬高第 K 大。
- 10push 10、弹最小 5 → 堆顶 5add(10):10 进堆、弹掉最小的 5,堆变 [5,8,10],堆顶还是 5。每次操作只做一次 push + 一次 pop,O(log k),远快过重排。
- 13记住骨架:拿一个大小恒为 K 的最小堆当「状态」,add 时 push 进去、若超 k 就 pop 掉最小,堆顶就是答案。
⚠️ 容易写错的地方
✗ 错:保存所有数据再排序
✓ 对:只维护 k 个最大值
数据流会不断增长
✗ 错:堆大小超过 k 不弹出
✓ 对:超过 k 就 heappop
堆顶才代表第 k 大
完整代码(Python / C++ / Java)
Python
import heapq
class KthLargest:
def __init__(self, k, nums):
self.k = k
self.heap = nums[:]
heapq.heapify(self.heap)
while len(self.heap) > k:
heapq.heappop(self.heap)
def add(self, val):
heapq.heappush(self.heap, val)
if len(self.heap) > self.k:
heapq.heappop(self.heap)
return self.heap[0]C++
class KthLargest {
int k;
priority_queue<int, vector<int>, greater<int>> pq; // 最小堆
public:
KthLargest(int k, vector<int>& nums) : k(k) {
for (int x : nums) add(x);
}
int add(int val) {
pq.push(val);
if ((int)pq.size() > k) pq.pop();
return pq.top();
}
};Java
class KthLargest {
int k;
PriorityQueue<Integer> pq = new PriorityQueue<>(); // 最小堆
public KthLargest(int k, int[] nums) {
this.k = k;
for (int x : nums) add(x);
}
public int add(int val) {
pq.offer(val);
if (pq.size() > k) pq.poll();
return pq.peek();
}
}复杂度
add 时间
O(log k)
每次一入堆一出堆,只动 log k 层,与总数 n 无关
初始化
O(n log k)
n 个数逐个入堆裁到 k(Python heapify 路线 O(n))
空间
O(k)
堆里恒定只留 k 个数
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 数据流中的第 K 大元素 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么不每次排序?+
每次 add 都排序是 O(n log n);用大小为 k 的最小堆,每次只 push+pop 一次 = O(log k),且只占 O(k) 空间。
为什么是最小堆不是最大堆?+
要的是「第 K 大」。只留最大的 K 个,这 K 个里最小的(最小堆堆顶)就是答案;最大堆堆顶是最大值,不对。
和「数组第 K 大 LC215」有何不同?+
LC215 是静态数组、一次性求第 K 大(可快速选择 O(n));本题是数据流、要反复 add 后立刻回答,最小堆的增量维护更合适。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。