Implement a MyCalendarTwo
class to store your events. A new event can be added if adding the event will not cause a triple booking.
Your class will have one method, book(int start, int end)
. Formally, this represents a booking on the half open interval [start, end)
, the range of real numbers x
such that start <= x < end
.
A triple booking happens when three events have some non-empty intersection (ie., there is some time that is common to all 3 events.)
For each call to the method MyCalendar.book
, return true
if the event can be added to the calendar successfully without causing a triple booking. Otherwise, return false
and do not add the event to the calendar.
MyCalendar cal = new MyCalendar();
MyCalendar.book(start, end)
Example 1:
MyCalendar(); MyCalendar.book(10, 20); // returns true MyCalendar.book(50, 60); // returns true MyCalendar.book(10, 40); // returns true MyCalendar.book(5, 15); // returns false MyCalendar.book(5, 10); // returns true MyCalendar.book(25, 55); // returns true
这道题咋一看意思好理解,但是要解出这道题还是需要有条理的思路。
1.首先我们需要定义两个set<int,int> s1,s2, 来分别存储没有交集(该区间被覆盖一次)、有一层交集(覆盖两次)的区间。
2.如果每次预订的区间与之前的所有区间没有交集(if(Start>=a.second || End<=a.first)),那么将其直接加入s1中;如果与map中某个区间有交集,那么将公共区间加入有一层交集的s2中。
3.最后每次先在s2中查看是否与当前区间有重叠,若有直接返回false,若无则查看s1中是否有与之重叠的区间,若有则更新s2,若无则更新s1。
如下为代码实现:
class MyCalendarTwo{
public :
MyCalendarTwo(){};
bool book(int Start,int End){
for(auto a:s2){
if(Start>=a.second || End<=a.first) continue;
else return false;
}
for(auto a:s1){
if(Start>=a.second || End<=a.first) continue;
else s2.insert({max(Start,a.first),min(End,a.second)});
}
s1.insert({Start,End});
return true;
}
private :
set<pair<int,int> > s1,s2;
};
下面将介绍另一种巧妙地方法(以下为转载),
解法:建立一个时间点和次数之间的映射,规定遇到起始时间点,次数加1,遇到结束时间点,次数减1。那么我们首先更改新的起始时间start和结束时间end的映射,start对应值增1,end对应值减1。然后定义一个变量cnt,来统计当前的次数。我们使用map具有自动排序的功能,所以我们遍历的时候就是按时间顺序的,最先遍历到的一定是一个起始时间,所以加上其映射值,一定是个正数。
举例:我们现在假设map中已经加入了一个区间[3, 5)了,那么我们就有下面的映射:
3 -> 1
5 -> -1
假如我们此时要加入的区间为[6, 8)的话,那么在遍历到6的时候,前面经过3和5,分别加1减1,那么cnt又重置为0了,而后面的6和8也是分别加1减1,还是0。那么加入我们新加入的区间为[3, 8]时,那么此时的映射为:
3 -> 2
5 -> -1
8 -> -1
那么我们最先遍历到3,cnt为2,没有超过3,我们知道此时有两个事件有重叠,是允许的。然后遍历5和8,分别减去1,最终又变成0了,始终cnt没有超过2,所以是符合题意的。如果此时我们再加入一个新的区间[1, 4),那么此时的映射为:
1 -> 1
3 -> 2
4 -> -1
5 -> -1
8 -> -1
那么我们先遍历到1,cnt为1,然后遍历到3,此时cnt为3了,那么我们就知道有三个事件有重叠区间了,所以这个新区间是不能加入的,那么我们要还原其start和end做的操作,把start的映射值减1,end的映射值加1,然后返回false。否则没有三个事件有共同重叠区间的话,返回true即可,参见代码如下:
class MyCalendarTwo {
public:
MyCalendarTwo() {}
bool book(int start, int end) {
++freq[start];
--freq[end];
int cnt = 0;
for (auto f : freq) {
cnt += f.second;
if (cnt == 3) {
--freq[start];
++freq[end];
return false;
}
}
return true;
}
private:
map<int, int> freq;
};