LeetCode 253. 会议室 II
题目描述
题意分析
输入是一组会议的起止时间
[start, end],每个会议必须独占一间会议室,问最少准备几间会议室才能把所有会议都排下。关键是把「最少几间」翻译成一个可计算的量:某一时刻同时进行的会议有几场,那一刻就至少要几间会议室;反过来,只要房间数达到了整条时间线上的最大同时进行数,就一定排得下——任意时刻的需求都不超过这个上限,永远有空房可分。所以答案等于区间的最大重叠数,一个纯统计量,与「具体把哪场会排进哪间屋」无关。
约束信号是区间半开:
[0, 30]与[30, 40]不算冲突,前一场结束的瞬间房间已经腾出来了。这决定了后面所有比较里,端点相等必须判为「可复用」。边界要留意:会议列表为空时答案为 0;只有一场会议时答案为 1;输入不保证按开始时间有序,也不保证区间互不相同,两场完全重合的会议仍要占两间房。
解法:开始结束时间双指针
核心思路
最少会议室数等于任意时刻的最大并发会议数:并发数给出房间数的下界;准备这么多房间后,每次会议结束就释放一间,也足以安排后续会议。
并发数只会在端点处变化,因此把开始时间和结束时间分别排序,用两个指针按时间合并事件。若下一个开始时间早于下一个结束时间,说明旧会议尚未结束,新会议必须多占一间,
active++;否则先处理结束事件并释放房间,active--。端点相等时也应先结束再开始,因为[0,10]与[10,20]可以复用同一间房。扫描中的不变量是:
active = 已处理的开始事件数 - 已处理的结束事件数,即当前正在进行的会议数;answer保存它的历史最大值。我们只统计事件数量,不关心某个开始时间对应哪个结束时间,所以拆开区间不会丢失答案所需的信息。
解题步骤
- 将所有区间拆成
starts、ends,分别升序排序。- 比较两个指针指向的下一个事件:开始更早就增加并发数,否则减少并发数。
- 只有开始事件会抬高峰值,此时更新答案;对应指针随后右移。
- 所有开始事件处理完后返回最大并发数。
对
[[0,30],[5,10],[15,20]],开始序列为[0,5,15],结束序列为[10,20,30]。合并出的关键事件是“开始、开始、结束、开始”,active依次为1、2、1、2,峰值为 2。
代码实现
import java.util.Arrays;
class Solution {
public int minMeetingRooms(int[][] intervals) {
int n = intervals.length;
if (n == 0) {
return 0;
}
int[] starts = new int[n];
int[] ends = new int[n];
for (int i = 0; i < n; i++) {
starts[i] = intervals[i][0];
ends[i] = intervals[i][1];
}
Arrays.sort(starts);
Arrays.sort(ends);
int startIdx = 0;
int endIdx = 0;
int active = 0;
int answer = 0;
while (startIdx < n) {
if (starts[startIdx] < ends[endIdx]) {
active++;
answer = Math.max(answer, active);
startIdx++;
} else {
active--;
endIdx++;
}
}
return answer;
}
}
import "sort"
func minMeetingRooms(intervals [][]int) int {
n := len(intervals)
if n == 0 {
return 0
}
starts := make([]int, n)
ends := make([]int, n)
for i, interval := range intervals {
starts[i] = interval[0]
ends[i] = interval[1]
}
sort.Ints(starts)
sort.Ints(ends)
startIdx := 0
endIdx := 0
active := 0
answer := 0
for startIdx < n {
if starts[startIdx] < ends[endIdx] {
active++
if active > answer {
answer = active
}
startIdx++
} else {
active--
endIdx++
}
}
return answer
}
复杂度分析
- 时间复杂度:$O(n \log n)$,排序占主导,双指针扫描为 $O(n)$。
- 空间复杂度:$O(n)$,保存两个端点数组。
关键点总结
- 最少资源数问题先转化为“最大同时占用数”,无需真的分配房间。
- 双指针本质是合并两组有序事件,
active始终表示当前重叠数。- 相同时间必须先处理结束事件,才能正确复用会议室。
- 若题目要求输出具体的房间分配,才需要改用最小堆保存房间结束时间。
易错点总结
- 用
starts[startIdx] <= ends[endIdx]判断开始事件,会把相接区间误判为重叠;[[0,10],[10,20]]只需一间房。- 任一端点数组未排序,两个指针就不再代表全局下一个事件;
[[10,20],[0,5]]应返回 1。- 返回扫描结束时的
active而不是历史峰值,会漏掉中途并发;[[0,30],[5,10],[40,50]]的峰值是 2。- 空输入时不能访问
ends[0],应先返回 0。
相似题目
| 题目 | 难度 | 考察点 |
|---|---|---|
| 252. 会议室 | 简单 | 只需判断是否存在重叠,排序后比较相邻区间即可 |
| 56. 合并区间 | 中等 | 要输出合并后的区间本身,必须保留配对关系 |
| 435. 无重叠区间 | 中等 | 求最少删除数,按结束时间排序做贪心而非统计重叠 |
| 1094. 拼车 | 中等 | 事件带权重,重叠数换成载客量之和,天然适配差分 |
| 1109. 航班预订统计 | 中等 | 纯差分数组模板,求的是每个位置的最终值而非峰值 |
| 732. 我的日程安排表 III | 困难 | 区间在线到来,需用有序表或线段树动态维护最大重叠数 |
| 759. 员工空闲时间 | 困难 | 求重叠数降为 0 的空档区间,扫描线的输出形态不同 |
| 1229. 安排会议日程 | 中等 | 求两组区间的交集且需满足时长下限,用双指针求交而非计数 |