1. 引言
在现代办公和协作中,会议室的高效利用至关重要。LeetCode 252 题“会议室 III”要求我们在给定一组会议的时间区间后,计算同一时间段内需要开的最少会议室数量,以保证所有会议能顺利进行。本题不仅是经典的区间调度问题变形,也常见于资源分配、调度系统等实战场景。
标签:
- 堆(Heap)
- 排序(Sort)
- 贪心(Greedy)
应用场景:
当系统中需动态分配资源(如服务器、房间、工程师),如何最小化资源数量而满足并发需求。该思路可推广到云计算资源调度、任务池管理等领域。
2. 题目描述
给定一个会议时间列表 intervals
,其中 intervals[i] = [start_i, end_i]
表示第 i
场会议的开始时间和结束时间(不含结束端点)。请你计算并返回同时进行的会议的最大数量,也就是需要开的最少会议室数量。
示例函数签名(Python):
def minMeetingRooms(intervals: List[List[int]]) -> int:
pass
- 输入:二维整数数组
intervals
,长度范围[0, 10^4]
。 - 输出:整数,最少会议室数量。
3. 示例分析
示例 1:
输入:[[0,30],[5,10],[15,20]]
输出:2
- 解释:会议 [0,30] 与 [5,10] 时间重叠,需要 2 个房间;[15,20] 可复用 [5,10] 结束的房间。
示例 2:
输入:[[7,10],[2,4]]
输出:1
- 解释:没有重叠,所有会议可在同一房间举行。
4. 问题建模与关键点
- 区间调度:本题属于「同时进行的区间最大重叠数」问题,经典解法是通过排序和优先队列统计并发量。
- 数据结构映射:
- 将每个会议的结束时间入堆,堆顶保存当前最早结束的会议。
- 遍历开始时间,从堆中弹出所有已结束的会议,堆的大小即为当前并发会议数。
5. 解题思路
- 暴力枚举:对每一个分割点遍历所有区间,时间复杂度 O(n²),不推荐。
- 最小堆 + 排序:
- 按会议开始时间升序排序。
- 使用最小堆维护各会议的结束时间。
- 对每个会议,比较堆顶结束时间与当前会议开始时间:
- 若堆顶 ≤ 当前开始,说明该会议室已空,将堆顶弹出并复用。
- 否则需新开会议室。
- 将当前会议的结束时间入堆。
- 堆的大小即为所需会议室数。
为什么用堆?
- 堆能在 O(log n) 内获得并更新最小结束时间,使整体复杂度保持 O(n log n)。
6. 算法流程
- 若
intervals
为空,返回 0。 - 将所有会议按
start
升序排序。 - 初始化最小堆
heap = []
。 - 遍历
intervals
:- 若
heap
非空且heap[0] ≤ current_start
,弹出堆顶(复用会议室)。 - 否则无需操作(开新会议室)。
- 将
current_end
入堆。
- 若
- 遍历结束后,
len(heap)
即为答案。
7. 代码实现
Python 版本
from typing import List
import heapq
def minMeetingRooms(intervals: List[List[int]]) -> int:
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)
Java 版本
import java.util.*;
class Solution {
public int minMeetingRooms(int[][] intervals) {
if (intervals == null || intervals.length == 0) return 0;
Arrays.sort(intervals, Comparator.comparingInt(a -> a[0]));
PriorityQueue<Integer> heap = new PriorityQueue<>();
for (int[] interval : intervals) {
if (!heap.isEmpty() && heap.peek() <= interval[0]) {
heap.poll();
}
heap.offer(interval[1]);
}
return heap.size();
}
}
8. 复杂度分析
- 时间复杂度:排序 O(n log n) + 遍历 n 次,每次堆操作 O(log n),总体 O(n log n)。
- 空间复杂度:最坏情况下所有会议重叠,堆中有 n 个元素,O(n)。
9. 边界情况 & 优化思考
- 空列表:直接返回 0。
- 单个会议:返回 1。
- 所有会议无重叠:堆大小最高保持 1。
- 大量重叠:堆大小趋近 n。
- 优化内存有限场景:可将结束时间存入有序数组并使用双指针,牺牲部分时间性能以节省堆空间。
10. 总结
- 本题为区间调度经典变形,核心在于统计最大并发区间数。
- 利用最小堆维护最早结束时间,实现 O(n log n) 解决。
- 模板可复用于「会议室 II」「区间交集」「区间插入」等相关题目。
11. 模拟面试
在面试中,本题常作为考察区间调度与堆应用的典型题目。
面试题型示例
- 题目复述:给定一组会议时间区间,求所需的最少会议室数。
- 核心考点:区间排序、最小堆维护、复杂度分析。
- 扩展追问:
- 如果数据量达到 10⁸ 条,如何优化?
- 在内存受限时,双指针法如何替代堆?
- 多线程场景下,如何并行调度?
答题建议流程
- 明确题意:复述输入输出,例举小规模示例。
- 思路阐述:先讲排序,再说明堆的作用和复用机制。
- 手写伪码:关键操作集中在堆弹出与入堆两步。
- 复杂度分析:O(n log n) 时间,O(n) 空间。
- 优化讨论:根据场景提出双指针、流式处理或并行方案。