题目描述
思路解析动画文字版
下面按主解代码逐帧推进:先把「大小为 K 的最小堆」建起来,再看每次 add 如何用 O(log k) 维护「第 K 大」。
目标 · 数据流里随时问「第 K 大」:这是一个数据流问题:数字不断加进来,每加一个就要立刻回答「目前第 K 大是多少」。每次都重新排序太慢,需要一个能动态维护的结构。
核心 · 维护一个大小为 K 的最小堆:诀窍:只留「目前最大的 K 个数」,用一个最小堆装它们。那么这 K 个里最小的(也就是堆顶),正好就是第 K 大。比它还小的数根本排不进前 K,不用管。
对照 · 为什么用「最小堆」而非「最大堆」:求「第 K 大」反而用「最小堆」——留住最大的 K 个,看它们当中最小的那个。
初始化 k=3 · 弹掉多余的小数:初始化:4、5、8、2 都进堆,但只能留 3 个,最小的 2 被弹掉。堆里剩 [4,5,8],堆顶 4 就是当前第 3 大。
add(3) · 比堆顶小,进了又被弹:add(3):先把 3 压进堆,超过 3 个了,弹掉最小的——正好就是刚进来的 3。堆顶还是 4,返回 4。比第 K 大还小的数,进来就被弹,毫无影响。
add(5) · 比堆顶大,挤掉旧的最小:add(5):5 压进堆,弹掉最小的 4。堆变成 [5,5,8],堆顶升到 5,返回 5。一个足够大的数进来,会把旧的最小挤出去、抬高第 K 大。
add(10) · 同理,堆顶仍 5:add(10):10 进堆、弹掉最小的 5,堆变 [5,8,10],堆顶还是 5。每次操作只做一次 push + 一次 pop,O(log k),远快过重排。
记住骨架:拿一个大小恒为 K 的最小堆当「状态」,add 时 push 进去、若超 k 就 pop 掉最小,堆顶就是答案。
边界三连:堆题边界:没攒够 k、小数无影响、重复各算一个。
三个高频追问:为何不排序、为何最小堆、和 LC215 区别。
参考代码
import heapqclass 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]复杂度
- add 时间:O(log k),每次一入堆一出堆,只动 log k 层,与总数 n 无关
- 初始化:O(n log k),n 个数逐个入堆裁到 k(Python heapify 路线 O(n))
- 空间:O(k),堆里恒定只留 k 个数
易错点
面试追问把动画讲成自己的话
追问为什么不每次排序?
追问为什么是最小堆不是最大堆?
追问和「数组第 K 大 LC215」有何不同?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
最后一块石头的重量
LeetCode 1046 · 简单 · 沿着 堆 / 优先队列 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题