题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
前置题目一:729. 我的日程安排表 I
前置题目一链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
前置题目一思路:
1、问题的关键在于,使用什么数据结构保存可插入的日程安排以及如何判定时间存在交叉,令s为已存在的start,e为已存在的end,当start>=e或end<=s时,无交叉,那么当start<e且end>s时有交叉。
2、使用List(int[])存放日程安排时,时间复杂度为n^2。
3、使用TreeSet<int[]>或Map<Integer, int[]>时,时间复杂度为nlogn。
4、线段树:考虑到0 <= start < end <= 10^9,时间粒度为1,那么使用数组int[10^9]该时间是否被预定,到时只需在数组的start~end查看有没有1即可。考虑到数组较大且时间是线性的,我们可以结合线段树与动态开点实现,对于一个数据范围l~r,线段树中从1到n的结点是与其有着一一对应的关系的,1——l~r,2——l~mid,3~mid+1~r,mid=(l+r)>>1,按照此规律一直二叉裂变下去;动态开点是指,当我们的时间点用到那些点的时候,才去开辟并保存这些点,避免占用内存过大。
具体做法是:
①lazy中保存已经被预定的区间对应的数字;
②tree保存存在被标记为1元素的区间;
③开点时,如果start和end不在此区间(即r < start || end < l)直接返回;如果start和end将此区间完全包围(即start <= l && r <= end),说明此区间是被预定的区间,lazy和tree都需要保存对应的数字;否则,是start和end与此区间有重合部分,那么此区间对应数字保存到tree中,并且将此区间裂变,在下一层结点继续进行上述操作,直至不在新区间或完全包围新区间。
④判断时,如果start和end不在此区间(即r < start || end < l)返回false;然后,查看当前区间是否被预定,即查看lazy中是否已经保存了当前区间对应的值;如果start和end将此区间完全包围(即start <= l && r <= end),则查看此区间是否有被标记的元素,即查看tree中是否保存了当前区间对应的值。否则,是start和end与此区间有重合部分,将此区间裂变,在新区间继续判断。
简单思路题解:
class MyCalendar {
Map<Integer, int[]> map;
public MyCalendar() {
this.map = new HashMap<>();
}
public boolean book(int start, int end) {
if(map.containsKey(start)){
return false;
}
for(Integer key:map.keySet()){
int[] temp = map.get(key);
if((start >temp[0] && start <temp[1]) || (start<temp[0] && end >temp[0])){
return false;
}
}
int[] can = new int[]{start, end};
map.put(start, can);
return true;
}
}
/**
* Your MyCalendar object will be instantiated and called as such:
* MyCalendar obj = new MyCalendar();
* boolean param_1 = obj.book(start,end);
*/
线段树思路题解:
class MyCalendar {
Set<Integer> lazy;
Set<Integer> tree;
public MyCalendar() {
this.lazy = new HashSet<>();
this.tree = new HashSet<>();
}
public boolean book(int start, int end) {
if(query(start,end-1,0,1000000000,1)){
return false;
}
update(start, end-1, 0, 1000000000,1);
return true;
}
void update(int start, int end, int l, int r, int idx){
if(start>r || end < l){
return;
}
if(start<=l && r<=end){
lazy.add(idx);
tree.add(idx);
} else {
int mid = (l+r)>>1;
update(start, end, l ,mid, 2*idx);
update(start, end, mid+1,r, 2*idx+1);
tree.add(idx);
}
}
boolean query(int start, int end, int l, int r, int idx){
if(start > r || end < l){
return false;
}
if(lazy.contains(idx)){
return true;
}
if(start <= l && r <= end){
return tree.contains(idx);
} else {
int mid = (l+r)>>1;
if(end <= mid){
return query(start, end, l, mid, 2*idx);
} else if( start > mid){
return query(start, end, mid+1, r, 2*idx+1);
} else {
return query(start, end, l, mid, 2*idx) | query(start, end, mid+1, r, 2*idx+1);
}
}
}
}
/**
* Your MyCalendar object will be instantiated and called as such:
* MyCalendar obj = new MyCalendar();
* boolean param_1 = obj.book(start,end);
*/
前置题目二:731. 我的日程安排表 II
前置题目二链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
前置题目二思路:
思路1:创建两个容器,一个存放预定成功的区间,一个放预定过两次的区间,每次的新时间先看是否有与两次的区间重合的,有则说明冲突,返回false,没有则遍历预定成功区间,找出预定过两次的区间并存入,再将新时间放入预定成功的区间,返回true。
class MyCalendarTwo {
List<int[]> booked;
List<int[]> twice;
public MyCalendarTwo() {
booked = new ArrayList<>();
twice = new ArrayList<>();
}
public boolean book(int start, int end) {
for(int[] arr: twice){
if(arr[0]<end && arr[1]>start){
return false;
}
}
for(int[] arr: booked){
if(arr[0]<end && arr[1]>start){
twice.add(new int[]{Math.max(arr[0],start), Math.min(arr[1],end)});
}
}
booked.add(new int[]{start, end});
return true;
}
}
/**
* Your MyCalendarTwo object will be instantiated and called as such:
* MyCalendarTwo obj = new MyCalendarTwo();
* boolean param_1 = obj.book(start,end);
*/
思路2:差分数组:预定时间进来,给start位置+1,表示以start为起点的时间区域有1次预定,给end位置-1,表示此次预定到此为止,对预定的影响也到此为止。对于新的预定时间,添加完成后,存在和>2的情况说明已经存在二重预定,需要回退并返回false。
class MyCalendarTwo {
TreeMap<Integer, Integer> map;
public MyCalendarTwo() {
map = new TreeMap<>();
}
public boolean book(int start, int end) {
map.put(start, map.getOrDefault(start, 0) + 1);
map.put(end, map.getOrDefault(end, 0) - 1);
int bookNum=0;
for(Integer key:map.keySet()){
bookNum += map.get(key);
if(bookNum > 2){
map.put(start, map.getOrDefault(start, 0) - 1);
map.put(end, map.getOrDefault(end, 0) + 1);
return false;
}
}
return true;
}
}
/**
* Your MyCalendarTwo object will be instantiated and called as such:
* MyCalendarTwo obj = new MyCalendarTwo();
* boolean param_1 = obj.book(start,end);
*/
思路3:线段树:
class MyCalendarTwo {
Map<Integer,int[]> map;
public MyCalendarTwo() {
map = new HashMap<>();
}
public boolean book(int start, int end) {
update(start, end-1, 0,1000000000,1,1);
if(map.get(1)[0]>2){
update(start, end-1, 0,1000000000,1,-1);
return false;
}
return true;
}
void update(int start, int end, int l, int r, int idx,int val){
if(start>r|| end <l){
return;
}
map.putIfAbsent(idx, new int[2]);
if(start<=l && end>=r){
map.get(idx)[0]+=val;
map.get(idx)[1]+=val;
} else {
int mid = (l+r)>>1;
update(start,end,l,mid,2*idx,val);
update(start,end,mid+1,r,2*idx+1,val);
map.putIfAbsent(2*idx, new int[2]);
map.putIfAbsent(2*idx+1, new int[2]);
map.get(idx)[0] = map.get(idx)[1]+Math.max(map.get(2*idx)[0], map.get(2*idx+1)[0]);
}
}
}
/**
* Your MyCalendarTwo object will be instantiated and called as such:
* MyCalendarTwo obj = new MyCalendarTwo();
* boolean param_1 = obj.book(start,end);
*/
732. 我的日程安排表 III
思路:在731的基础之上,不做几重的限制,添加后记录最高的重数即可。
class MyCalendarThree {
TreeMap<Integer,Integer> map;
public MyCalendarThree() {
map = new TreeMap<>();
}
public int book(int startTime, int endTime) {
int res = 0;
int bookNum = 0;
map.put(startTime, map.getOrDefault(startTime, 0)+1);
map.put(endTime, map.getOrDefault(endTime, 0)-1);
for(Integer key:map.keySet()){
bookNum += map.get(key);
res = Math.max(bookNum, res);
}
return res;
}
}
/**
* Your MyCalendarThree object will be instantiated and called as such:
* MyCalendarThree obj = new MyCalendarThree();
* int param_1 = obj.book(startTime,endTime);
*/
class MyCalendarThree {
Map<Integer,Integer> map;
Map<Integer,Integer> map2;
public MyCalendarThree() {
map = new HashMap<>();
map2 = new HashMap<>();
}
public int book(int startTime, int endTime) {
int res = 0;
update(startTime,endTime-1,0,1000000000,1);
return map.get(1);
}
void update(int start,int end, int l, int r,int idx){
if(start>r || end<l){
return;
}
if(start<=l && end>=r){
map.put(idx, map.getOrDefault(idx, 0)+1);
map2.put(idx, map2.getOrDefault(idx, 0)+1);
} else {
int mid = (r+l)>>1;
update(start, end, l, mid, 2*idx);
update(start, end, mid+1,r, 2*idx+1);
map.put(idx, map2.getOrDefault(idx, 0)+Math.max(map.getOrDefault(2*idx,0),map.getOrDefault(2*idx+1,0)));
}
}
}
/**
* Your MyCalendarThree object will be instantiated and called as such:
* MyCalendarThree obj = new MyCalendarThree();
* int param_1 = obj.book(startTime,endTime);
*/