携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第20天,点击查看活动详情
题目描述
实现一个 MyCalendar 类来存放你的日程安排。如果要添加的时间内不会导致三重预订时,则可以存储这个新的日程安排。
MyCalendar 有一个 book(int start, int end) 方法。它意味着在 start 到 end 时间内增加一个日程安排,注意,这里的时间是半开区间,即 [start, end), 实数 x 的范围为, start <= x < end。
当三个日程安排有一些时间上的交叉时(例如三个日程安排都在同一时间内),就会产生三重预订。
每次调用 MyCalendar.book 方法时,如果可以将日程安排成功添加到日历中而不会导致三重预订,返回 true。否则,返回 false 并且不要将该日程安排添加到日历中。
请按照以下步骤调用MyCalendar 类:
MyCalendar cal = new MyCalendar();
MyCalendar.book(start, end)
解题思路——双数组
在《我的日程安排Ⅰ》中,使用一个数组来记录 已预定的日程,随后每次添加都和 已预定日程数组 中的数据进行一一比对,如果发现重复预定则不允许添加。那么这道题在此基础上 允许二重预定,但是 不允许三重预定。因此我们可以使用 另一个数组,专门存放二重预定的日程,具体步骤:
- 每次添加新日程时,先去 二重预定数组 中查找是否发生重复预定。
- 如果找到重叠部分,则说明发生了三种预定,直接返回
false。 - 如果没有发生三种预定,那么我们去遍历 已预定数组,判断有没有发生二重预定,如果发生二重预定,则要将 重复的日程 添加到二重预定数组中,同时还应该将当前的新日程添加到已预定数组中,然后返回
false。
这样我们就能准确过滤掉三重预定,确保已预定日程和二重预定日程啦。
题解
var MyCalendarTwo = function() {
this.schedule = []
this.overlapSchedule = [];
};
/**
* @param {number} start
* @param {number} end
* @return {boolean}
*/
MyCalendarTwo.prototype.book = function(start, end) {
for(const plan of this.overlapSchedule) {
if(start < plan[1] && end > plan[0]) {
return false;
}
}
for(const plan of this.schedule) {
if(start < plan[1] && end > plan[0]) {
this.overlapSchedule.push([Math.max(start, plan[0]), Math.min(end, plan[1])]);
}
}
this.schedule.push([start, end]);
return true;
};
这里需要注意的是,二重预定的添加范围一定是 [Math.max(start, plan[0]), Math.min(end, plan[1])],有的小伙伴可能不是很理解这块,我们看个图就懂了:
来看看效率:
解题思路——差分数组(插旗法)
有关注之前 手摸手提桶跑路——LeetCode729.我的日程安排Ⅰ - 掘金 (juejin.cn) 的小伙伴们应该懂这个解法的思路啦。
我们会假设每个区域的起始位置为 1 和 -1,正常没有重复的情况下,起始位置到结束位置的和为 1+(-1)=0,如果说新增区域和某个区域有重叠,那么必然会存在区域的起始位置到结束位置的和 大于1 的情况。
这道题我们把和改为大于2就可以了。
题解
var MyCalendarTwo = function() {
this.schedule = Object.create(null);
};
MyCalendarTwo.prototype.book = function(start, end) {
let cnt = 0;
this.schedule[start]++ || (this.schedule[start] = 1)
this.schedule[end]-- || (this.schedule[end] = -1)
for(let k in this.schedule) {
cnt += this.schedule[k];
if(cnt > 2) {
this.schedule[start]--;
this.schedule[end]++;
return false
}
}
return true;
};