一、题目简介
在协作办公和云资源调度场景中,如何高效利用有限资源是面试高频考点。LeetCode 253《会议室 II》就是一道经典的**最大区间重叠(最大并发)**问题——统计同一时刻最多有多少会议重叠,也就是最少需要多少间会议室。
二、题目描述
给定一个会议时间数组 intervals
,其中每个 intervals[i] = [start_i, end_i]
表示会议的起止时间(不含结束端点)。请返回最少需要的会议室数量,保证所有会议不会重叠。
三、示例分析
示例1:
输入:[[0,30],[5,10],[15,20]]
输出:2
- [0,30] 和 [5,10] 重叠,需要2间会议室。
- [15,20] 可复用[5,10]的会议室,最大并发为2。
示例2:
输入:[[7,10],[2,4]]
输出:1
- 两个会议无重叠,只需1间会议室。
四、解题思路与详细步骤
方案一:扫描线 + 双指针
步骤详解
- 分解数据
- 把所有会议的“开始时间”提取到一个数组
starts
,“结束时间”提取到ends
。
- 把所有会议的“开始时间”提取到一个数组
- 排序
- 对
starts
和ends
分别升序排序,实现“时间轴”遍历。
- 对
- 初始化指针与变量
- 指针
s
、e
分别指向starts
、ends
的第一个元素。 used
表示当前在开的会议数量,max_rooms
记录最大并发数。
- 指针
- 模拟会议动态
- 遍历所有“开始”事件:
- 如果
starts[s] < ends[e]
,有新会议开始,used += 1
,s += 1
。 - 否则,有会议结束,
used -= 1
,e += 1
。 - 每步都
max_rooms = max(max_rooms, used)
。
- 如果
- 遍历所有“开始”事件:
- 返回max_rooms作为结果。
代码实现(含注释)
def minMeetingRooms(intervals):
if not intervals:
return 0
starts = sorted(i[0] for i in intervals)
ends = sorted(i[1] for i in intervals)
s = e = used = max_rooms = 0
while s < len(intervals):
if starts[s] < ends[e]:
used += 1
max_rooms = max(max_rooms, used)
s += 1
else:
used -= 1
e += 1
return max_rooms
核心思路举例
输入[[0, 30], [5, 10], [15, 20]]
starts = [0, 5, 15]
ends = [10, 20, 30]
- 0<10,会议室+1=1
- 5<10,再加1=2
- 15>=10,释放会议室-1=1,然后15<20,再加1=2
- max_rooms=2
方案二:最小堆法
步骤详解
- 按开始时间排序所有会议。
- 初始化小根堆heap,存放正在进行会议的“结束时间”。
- 遍历每个会议
- 如果heap非空且heap[0](最早结束时间)≤当前会议开始时间,则弹出heap顶(复用会议室)。
- 把当前会议结束时间加入heap(开新会议室或复用)。
- 堆的最大大小就是所需会议室数量。
代码实现(含注释)
import heapq
def minMeetingRooms(intervals):
if not intervals:
return 0
intervals.sort(key=lambda x: x[0])
heap = []
for start, end in intervals:
if heap and heap[0] <= start:
heapq.heappop(heap) # 释放会议室
heapq.heappush(heap, end) # 占用新会议室或复用
return len(heap)
举例分析
以[[0, 30], [5, 10], [15, 20]]
为例:
- 插入30(heap=[30])
- 5<30,插入10(heap=[10,30])
- 15≥10,弹出10再插入20(heap=[20,30])
- 最终堆大小为2
五、复杂度分析
- 时间复杂度:两种方法均为O(n log n),排序主导。
- 空间复杂度:O(n),因为需要存储开始/结束时间或堆。
六、边界与细节注意
- 空输入返回0
- 全部无重叠返回1
- 全部完全重叠返回n
- 结束时间等于下一个会议开始时间不算重叠,可复用会议室
七、模拟面试环节及答案
1. 问:如果你不能使用额外空间(比如新数组或堆)怎么优化?
答:
可以尝试原地排序和双指针,或者用原始数组存储和遍历,但实际业务与面试普遍允许O(n)级空间,只要表达你对空间优化的理解即可。极致优化下,可以通过原地排序,s/e指针遍历实现,但可读性变差。
2. 问:如果会议数据流式到来,怎么动态维护最少会议室数?
答:
实时维护一个最小堆,堆存“正在进行”会议的结束时间。每来一个新会议时,堆顶≤新会议开始则释放,否则新开会议室。可达到O(log n)实时处理。
3. 问:如果有千万级别会议,怎么优化内存和性能?
答:
用归并排序/外部排序降低内存,堆法和扫描线法都只存关键指针和必要计数。极端大数据场景可以分片并行计算,再合并区间最大值。
4. 问:多线程环境下如何并行或分布式处理?
答:
可将会议按时间或物理地点分片分区,各线程单独处理,最后归并所有分片的最大并发结果即可。还可MapReduce所有事件(time, +1/-1),全局排序后遍历一次归约最大并发。
5. 问:会议结束时间等于下一个会议开始时间,是否重叠?
答:
不重叠,完全可以复用同一个会议室。因为题目定义“结束端点不包含”,即前一个会议10点结束,后一个会议10点开始不冲突。
八、总结升华
LeetCode 253不仅考查算法,更是实际并发资源分配和高效调度问题的精华建模。掌握扫描线和堆两种方法,能解决绝大多数区间并发、调度、资源分配的高频面试题和实际场景。建议刷题者多用画表、模拟、举例法辅助理解,遇到区间类题目灵活套用本题套路即可高效答题。