253. 会议室 II
给你一个会议时间安排的数组 intervals ,每个会议时间都会包括开始和结束的时间 intervals[i] = [starti, endi] ,返回 所需会议室的最小数量 。
示例 1:
输入:intervals = [[0,30],[5,10],[15,20]]
输出:2
示例 2:
输入:intervals = [[7,10],[2,4]]
输出:1
提示:
1 <= intervals.length <= 104
0 <= starti < endi <= 106
解题
此题本质上还是比较一个会议的结束时间 和 下一个会议的开始时间的关系,如果结束时间大于开始时间,则需要的会议室数就加1。所以我们需要先按开始时间排序,然后遍历,比较结束时间与开始时间
方法一:利用小顶堆
小根堆的元素数量表示从开始到结束这个范围上重合的区间的最大值; 本质上就是求开始到结束范围上的重合区间数量最大值。
思路:先把数组按照开始时间升序,然后使用最小堆的元素个数表示重叠的区间数量; 堆中压入结束时间; 如果当前的开始时间大于等于最小堆堆顶的结束时间, 说明有一个会议开完了, 弹出堆顶元素
class Solution {
public int minMeetingRooms(int[][] intervals) {
//对数组按照开始时间升序排序
Arrays.sort(intervals, (a,b)->a[0]-b[0]);
//最小堆中存放结束时间; 最小堆中的元素数量表示从开始到结束重合的区间数量的最大值
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
for(int i=0; i<intervals.length; i++){
//如果最小堆不为空,并且当前时间段的开始时间大于等于堆顶元素, 说明之前有个会议结束了, 弹出该堆顶元素, 表示会议室数量减一
if(!minHeap.isEmpty() && intervals[i][0]>=minHeap.peek()) {
minHeap.poll();
}
minHeap.add(intervals[i][1]);
}
return minHeap.size();
}
}
方法二:上车下车方法
将开始时间、结束时间分别存放进两个数组,把开始时间升序排序, 把结束时间升序排序;
遍历开始时间, 同时用endp指向第一个结束时间;
- 如果当前的开始时间小于结束时间, 表示需要一间会议室, res++;
- 如果当前的开始时间大于等于结束时间, 说明之前有一个会议结束了, res–, 当前这个会议又需要一间会议室, res++, 所以res不变, endp++;
实质上是找开始到结束范围上的重合区间数量
class Solution {
public int minMeetingRooms(int[][] intervals) {
int n = intervals.length;
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 res = 0;
int endp = 0;
for(int i=0; i<n; i++){
//如果开始时间小于ends[endp]说明需要一间会议室
if(starts[i] < ends[endp])
res++;
else{
//说明之前有个会议结束了, 使用的会议室数量减一,
//同时当前这个时间又开始了一个会议, 使用的会议室数量加一;
// 相当于使用的会议室数量不变
// res--;
// res++;
endp++;
}
}
return res;
}
}
例子:
input: [[26,29],[19,26],[19,28],[4,19],[4,25]]
对开始时间排序:starts=[4,4,19,19,26]
对结束时间排序:ends=[19,25,26,28,29]
初始化res=0, endp=0
具体地遍历过程
strats[0] < ends[0], res++
strats[1] < ends[0], res++
strats[2] >= ends[0], res--, res++, endp++
strats[3] < ends[1], res++
strats[4] >= ends[1], res--,res++, endp++
方法三:使用哈希表
使用哈希表(TreeMap), key是时间, value是映射值(需要会议室的数量),
- 具体地, 当key是开始时间时, 映射值++;
- 当key是结束时间时, 映射值–;
cur记录当前需要的房间数, res记录最终需要的房间数, 其实res就是cur所有值中的最大值
遍历TreeMap(会按照key的自然顺序排序, 本题中也就是按照时间从早到晚排序), cur = cur + 当前时间对应的映射值; 这道题实质上是找开始到结束范围上的重合区间数量
class Solution {
public int minMeetingRooms(int[][] intervals) {
//TreeMap按照自然顺序排序, 对于int来说就是从小到大排序
TreeMap<Integer, Integer> map = new TreeMap<>();
for(int[] arr : intervals){
if(!map.containsKey(arr[0])) {
map.put(arr[0], 0);
} else {
map.put(arr[0], map.get(arr[0])+1);
}
if(!map.containsKey(arr[1])) {
map.put(arr[1], 0);
} else {
map.put(arr[1], map.get(arr[1])-1);
}
}
//记录当前需要的房间数
int cur = 0;
//记录最终的结果
int res = 0;
//遍历TreeMap
for(Map.Entry<Integer, Integer> e : map.entrySet()){
int key = e.getKey();
int val = e.getValue();
//
cur = cur + val;
res = Math.max(res, cur);
}
return res;
}
}
例子:
第一个例子: Input: [[0, 30],[5, 10],[15, 20]]
遍历TreeMap阶段如下图所示,
0处需要一间会议室,
5处又需要一间会议室, 此时我们需要两间会议室;
10处会减少一间会议室, 也就是只需要一间会议室了,
15处需要一间会议室, 此时我们需要两间会议室;
20处减少一间会议室, 此时我们需要一间会议室
30处减少一间会议室, 此时我们需要一间会议室
上述过程中, 我们最多需要两间会议室