题目描述:
给定一个会议时间安排的数组
intervals,每个会议时间包括开始和结束的时间[start, end],计算至少需要多少间会议室才能满足所有会议安排。示例:
输入: [[0, 30], [5, 10], [15, 20]] 输出: 2 输入: [[7, 10], [2, 4]] 输出: 1
解题思路:
方法一:排序 + 小根堆(推荐)
核心思路:
按开始时间排序所有会议
用小根堆维护当前正在使用的会议室的结束时间
遍历每个会议:
如果堆顶(最早结束的会议)≤ 当前会议的开始时间,说明有会议室空闲,弹出堆顶
把当前会议的结束时间压入堆
堆的大小就是所需的最少会议室数量
具体过程示例:
intervals = [[0, 30], [5, 10], [15, 20]]
排序后:[[0, 30], [5, 10], [15, 20]]
| 会议 | 堆操作 | 堆内容 | 说明 |
|---|---|---|---|
| [0, 30] | push(30) | [30] | 第一间会议室 |
| [5, 10] | 堆顶30 > 5,push(10) | [10, 30] | 需要第二间 |
| [15, 20] | 堆顶10 ≤ 15,pop,push(20) | [20, 30] | 复用第一间 |
堆大小 = 2 ✅
代码实现:
class Solution { public: int minMeetingRooms(vector<vector<int>>& intervals) { if (intervals.empty()) return 0; // 按开始时间排序 sort(intervals.begin(), intervals.end()); // 小根堆,存储结束时间 priority_queue<int, vector<int>, greater<int>> pq; for (auto& interval : intervals) { // 如果有会议室空闲,弹出最早结束的 if (!pq.empty() && pq.top() <= interval[0]) { pq.pop(); } pq.push(interval[1]); } return pq.size(); } };复杂度分析:
| 维度 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n log n) | 排序 + 每个会议一次堆操作 |
| 空间复杂度 | O(n) | 堆最多存储 n 个结束时间 |
方法二:扫描线
思路:
把每个会议拆成两个事件:
开始:+1(需要一间会议室)
结束:-1(释放一间会议室)
按时间排序所有事件,累加计数,最大值就是所需会议室数。
关键:同一时间有开始和结束,结束优先处理(先释放再占用)。
具体过程示例:
intervals = [[0, 30], [5, 10], [15, 20]]
事件:(0,+1), (5,+1), (10,-1), (15,+1), (20,-1), (30,-1)
| 时间 | 事件 | 计数 | 最大值 |
|---|---|---|---|
| 0 | +1 | 1 | 1 |
| 5 | +1 | 2 | 2 |
| 10 | -1 | 1 | 2 |
| 15 | +1 | 2 | 2 |
| 20 | -1 | 1 | 2 |
| 30 | -1 | 0 | 2 |
结果:2✅
代码实现:
class Solution { public: int minMeetingRooms(vector<vector<int>>& intervals) { vector<pair<int, int>> events; for (auto& interval : intervals) { events.push_back({interval[0], 1}); // 开始 events.push_back({interval[1], -1}); // 结束 } // 按时间排序,同一时间结束优先(-1 在前) sort(events.begin(), events.end(), [](const pair<int,int>& a, const pair<int,int>& b) { if (a.first == b.first) return a.second < b.second; return a.first < b.first; }); int count = 0, maxRooms = 0; for (auto& [time, delta] : events) { count += delta; maxRooms = max(maxRooms, count); } return maxRooms; } };复杂度分析:
| 维度 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n log n) | 排序事件 |
| 空间复杂度 | O(n) | 事件数组 |
方法三:双指针
思路:
把开始时间和结束时间分别排序,用两个指针比较:
如果
start[s] < end[e]:新会议开始,需要新会议室,count++,s++否则:有会议结束,释放会议室,
count--,e++
代码实现:
class Solution { public: int minMeetingRooms(vector<vector<int>>& intervals) { int n = intervals.size(); vector<int> starts(n), ends(n); for (int i = 0; i < n; i++) { starts[i] = intervals[i][0]; ends[i] = intervals[i][1]; } sort(starts.begin(), starts.end()); sort(ends.begin(), ends.end()); int s = 0, e = 0, count = 0, maxRooms = 0; while (s < n) { if (starts[s] < ends[e]) { count++; s++; } else { count--; e++; } maxRooms = max(maxRooms, count); } return maxRooms; } };复杂度分析:
| 维度 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n log n) | 排序两个数组 |
| 空间复杂度 | O(n) | 两个辅助数组 |
三种方法对比:
| 方法 | 时间复杂度 | 空间复杂度 | 代码复杂度 | 推荐度 |
|---|---|---|---|---|
| 排序 + 小根堆 | O(n log n) | O(n) | 中等 | ⭐⭐⭐⭐⭐ |
| 扫描线 | O(n log n) | O(n) | 中等 | ⭐⭐⭐⭐ |
| 双指针 | O(n log n) | O(n) | 简单 | ⭐⭐⭐⭐ |
总结:
| 要点 | 说明 |
|---|---|
| 核心思想 | 求重叠会议的最大数量 |
| 最优解法 | 排序 + 小根堆(维护结束时间) |
| 关键操作 | 堆顶 ≤ 当前开始时间 → 复用会议室 |
| 时间复杂度 | O(n log n) |
| 空间复杂度 | O(n) |