题目描述
思路解析动画文字版
笨办法是给每间房记完整日程,来一个会议就从头扫所有房间找空位,会反复检查很多已经结束的会议。
转折:按开始时间排序,用小根堆存正在占用的房间结束时间;堆顶就是最早结束的那间,能不能复用只看它。
堆是一棵二叉树:堆顶永远是最小值:先认识堆:它不是排好序的数组,而是一棵二叉树,最小值永远浮在树根。我们每次只看树根这一间房。
按开始时间排序,准备从最早的会议开始扫:按开始时间排序,才能在每个会议开始前,正好释放掉那些已经结束的房间。
会议 [0,30]:堆是空树,直接占一间房:第一场会议来时堆是空的,直接开一间房,把结束时间 30 放进树根。
会议 [5,10]:树根 30 > 5,根这间房还没空:5 点要开会,但树根那间房要到 30 才散,所以不能复用,必须再开一间。
push 10:新房入堆,10 比 30 小,上浮成新树根:新房的结束时间 10 比旧根 30 小,自动上浮到树根。现在树有两层,同时占用 2 间房。
会议 [15,20]:树根 10 ≤ 15,这间房空了,弹出:15 点开会前,先看树根 10——它早就散了,弹出它,这间房可以让给新会议。
push 20:复用了刚空的房,峰值仍是 2:新会议复用了刚腾出的房间,树又是两个节点,所以同时占用的房间数没有再涨。
扫描结束:答案是历史最大并发,不是最后的堆大小:答案是整个扫描过程里同时占用房间的峰值,而不是最后剩几个,更不是会议总数。
极端例:三个会议全重叠,堆长成两层二叉树:三场会议时间全重叠时谁也腾不出来,堆长成根加两个孩子的二叉树,根始终是最早结束的 30,答案就是 3。
会议室 II 是“最小资源数 = 最大同时占用数”的代表题,堆只负责高效追踪最早结束的那间房。
边界三连:边界先看最小规模、端点相接,和最容易触发分支的全重叠样例。
面试追问:把动画里的状态定义、while 释放和替代解法讲成自己的话,就能扛住追问。
这是“最大重叠资源数”的原子题:往上是会议室 I 判重叠、合并区间、用机场起降;想系统刷堆与区间,去 堆专题 或 区间专题,也可以直接问小欧帮你串题。
参考代码
import heapqclass Solution: def minMeetingRooms(self, intervals): intervals.sort(key=lambda x: x[0]) heap = [] ans = 0 for start, end in intervals: while heap and heap[0] <= start: heapq.heappop(heap) heapq.heappush(heap, end) ans = max(ans, len(heap)) return ans复杂度
- 时间复杂度:O(n log n),排序 O(n log n);每个会议入堆出堆各一次,单次堆操作 O(log n)。
- 空间复杂度:O(n),最坏所有会议重叠,堆这棵二叉树里有 n 个结束时间。
可套用的代码模板
把本题抽象成四步骨架——起点排序、起点前释放、占用、刷新峰值;换成出租车调度、CPU 任务并发都套这套。右上角可切 C++ / Java。
# 区间并发峰值 = 最少资源数items.sort(key=lambda x: x[0]) # 1) 按区间起点排序heap = [] # 小根堆存“占用中资源”的结束点peak = 0for s, e in items: while heap and heap[0] <= s: # 2) 起点前,释放已结束的资源 heapq.heappop(heap) heapq.heappush(heap, e) # 3) 占用一个资源 peak = max(peak, len(heap)) # 4) 刷新历史最大并发return peak易错点
面试追问把动画讲成自己的话
追问堆里维护的到底是什么?
追问为什么释放用 while 而不是 if?
追问不用堆还能怎么做?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
包含每个查询的最小区间
LeetCode 1851 · 困难 · 沿着 区间 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题