我的日程安排表 I
题意
实现一个 MyCalendar 类来存放你的日程安排。如果要添加的日程安排不会造成 重复预订 ,则可以存储这个新的日程安排。
当两个日程安排有一些时间上的交叉时(例如两个日程安排都在同一时间内),就会产生 重复预订 。
日程可以用一对整数 start 和 end 表示,这里的时间是半开区间,即 [start, end), 实数 x 的范围为, start <= x < end 。
实现 MyCalendar 类:
MyCalendar() 初始化日历对象。 boolean book(int start, int end) 如果可以将日程安排成功添加到日历中而不会导致重复预订,返回 true 。否则,返回 false 并且不要将该日程安排添加到日历中。
示例:
输入: ["MyCalendar", "book", "book", "book"] [[], [10, 20], [15, 25], [20, 30]] 输出: [null, true, false, true]
解释: MyCalendar myCalendar = new MyCalendar(); myCalendar.book(10, 20); // return True myCalendar.book(15, 25); // return False ,这个日程安排不能添加到日历中,因为时间 15 已经被另一个日程安排预订了。 myCalendar.book(20, 30); // return True ,这个日程安排可以添加到日历中,因为第一个日程安排预订的每个时间都小于 20 ,且不包含时间 20 。
提示:
0 <= start < end <= 109 每个测试用例,调用 book 方法的次数最多不超过 1000 次。
相关类型: 设计、线段树、二分查找、有序集合
温馨提示:
- Store the events as a sorted list of intervals. If none of the events conflict, then the new event can be added.
AC代码
👀java版:
class MyCalendar {
Map<Integer,Integer> map;
List<Integer> set;
int len;
public MyCalendar() {
map = new HashMap<>();
set = new ArrayList<Integer>();
len =0;
}
public boolean book(int start, int end) {
int index = Collections.binarySearch(set,start);
if(index>=0) return false;
if(index==-1){
int startOld = 0;
if(len!=0) startOld = set.get(0);
if(len==0||end<=startOld) {
set.add(-index-1,start);
map.put(start,end);
len++;
return true;
}else return false;
}else if(-index==(len+1)){
int startPre = set.get(-index-2);
int endPre = map.get(startPre);
if(endPre<=start){
set.add(-index-1,start);
map.put(start,end);
len++;
return true;
}else return false;
}else{
int startPre = set.get(-index-2);
int endPre = map.get(startPre);
int startnext = set.get(-index-1);
int endnext = map.get(startnext);
if(endPre<=start&&startnext>=end){
set.add(-index-1,start);
map.put(start,end);
len++;
return true;
}
else return false;
}
}
}
/**
* Your MyCalendar object will be instantiated and called as such:
* MyCalendar obj = new MyCalendar();
* boolean param_1 = obj.book(start,end);
*/
👀C++代码:
class MyCalendar {
public:
map<int, int> mp;
MyCalendar() {
}
bool book(int start, int end) {
auto temp = mp.lower_bound(start);
if (temp != mp.end()){
if (end > temp->first) return false;
}
if (temp != mp.begin()){
temp --;
if (temp->second > start) return false;
}
mp[start] = end;
return true;
}
};
/**
* Your MyCalendar object will be instantiated and called as such:
* MyCalendar* obj = new MyCalendar();
* bool param_1 = obj->book(start,end);
*/
分析
首先根据题意知道在可插入的时候,每次插入之后需要是有序的,如果暴力解决的话,每次插入时候从头遍历到最后,直到找到符合的位置,当然使用List<int[]> 存储每一个节点,使用暴力的方法也能通过,但是时间复杂度相对较高。
对于这道题,我才用的解决方法是用list<> 存储的每个节点的开始节点,用map存储的每个节点的开始位置和结束位置,key为开始位置,value为结束位置,每次使用二分查找找到插入的新范围在list中的位置,然后找到插入位置前后的start,进行判断,是否符合,当然,也要考虑在最初的开头位置和结尾位置进行插入这两种特殊情况。
题解过程分析
- map = new HashMap<>(); ,set = new ArrayList();分别定义map和set存储相应的值。
- int index = Collections.binarySearch(set,start); 通过二分查找找到应该插入的对应的位置,应该注意负数表示插入的位置的相反数,需要把负数反过来,返回正数表示会重复。
- if(index==-1) 这个表示在开头的位置的时候进行相应的处理,在这个情况里注意刚开始set和map为空的时候,插入的时候不用比较。
- else if(-index==(len+1))这个表示的是在最后插入的情况,这个要注意的是len是用来记录set中的个数。
- else 表示前后都有数值的时候,需要start和前边范围的结束位置进行比较,end和后边范围的开始位置进行比较。
图解过程分析
- 未找到,即没有比
end大的区间,那么我们和最后一个区间比较right1是否小于等于start,是返回true,否则返回false;
- 是第一个区间,直接插入
- 中间区间,我们找到last[left1,right1]的上一个区间pre[left2,right2],若right2大于start,证明有交集,返回false,否则返回true
复杂度分析
复杂度分析如下:
- 时间复杂度:O(nlogn)
- 空间复杂度:O(3n) :主要是含有list的存储需要和map中的存储需要
- 如果对您有帮助,记得关注 + 点赞 + 收藏呦!!!
- 每天都会更新每日一题题解,大家加油!!!
总结
总的来看这种类型的题目,常见的解决方法有两种,一种是使用list数组存储对应的数字对,另一种是使用线段树的区间,相比于线段树的代码复杂度比较高,不太容易理解,二分法的方法容易理解,但是其耗费的时间复杂度相对高一点,如果使用上边的我写的那种方法,注意的是二分法如果不是自己手写的话,需要注意自带的binarySearch返回的位置为正数的时候表示查找到了元素的位置,如果返回的是负值,表示需要在对应相反数的位置插入,比如-2表示在第二个位置插入,也就是下标为1的位置。
我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿