每日一刷经验分享:力扣729中等-我的日程安排表 I

123 阅读3分钟

我的日程安排表 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 次。

相关类型: 设计线段树二分查找有序集合

温馨提示:

  1. 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,进行判断,是否符合,当然,也要考虑在最初的开头位置和结尾位置进行插入这两种特殊情况。

题解过程分析

  1. map = new HashMap<>(); ,set = new ArrayList();分别定义map和set存储相应的值。
  2. int index = Collections.binarySearch(set,start); 通过二分查找找到应该插入的对应的位置,应该注意负数表示插入的位置的相反数,需要把负数反过来,返回正数表示会重复。
  3. if(index==-1) 这个表示在开头的位置的时候进行相应的处理,在这个情况里注意刚开始set和map为空的时候,插入的时候不用比较。
  4. else if(-index==(len+1))这个表示的是在最后插入的情况,这个要注意的是len是用来记录set中的个数。
  5. else 表示前后都有数值的时候,需要start和前边范围的结束位置进行比较,end和后边范围的开始位置进行比较。

图解过程分析

  1. 未找到,即没有比end大的区间,那么我们和最后一个区间比较right1是否小于等于start,是返回true,否则返回false;

image.png

  1. 是第一个区间,直接插入

image.png

  1. 中间区间,我们找到last[left1,right1]的上一个区间pre[left2,right2],若right2大于start,证明有交集,返回false,否则返回true

image.png

复杂度分析

复杂度分析如下:

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(3n) :主要是含有list的存储需要和map中的存储需要
  • 如果对您有帮助,记得关注 + 点赞 + 收藏呦!!!
  • 每天都会更新每日一题题解,大家加油!!!

总结

总的来看这种类型的题目,常见的解决方法有两种,一种是使用list数组存储对应的数字对,另一种是使用线段树的区间,相比于线段树的代码复杂度比较高,不太容易理解,二分法的方法容易理解,但是其耗费的时间复杂度相对高一点,如果使用上边的我写的那种方法,注意的是二分法如果不是自己手写的话,需要注意自带的binarySearch返回的位置为正数的时候表示查找到了元素的位置,如果返回的是负值,表示需要在对应相反数的位置插入,比如-2表示在第二个位置插入,也就是下标为1的位置。

image.png

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿