[LeetCode] My Calendar I 我的日歷之一


 

Implement a MyCalendar class to store your events. A new event can be added if adding the event will not cause a double booking.

Your class will have the 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.

double booking happens when two events have some non-empty intersection (ie., there is some time that is common to both events.)

For each call to the method MyCalendar.book, return true if the event can be added to the calendar successfully without causing a double booking. Otherwise, return false and do not add the event to the calendar.

Your class will be called like this:  MyCalendar cal = new MyCalendar();  MyCalendar.book(start, end)

Example 1:

MyCalendar();
MyCalendar.book(10, 20); // returns true
MyCalendar.book(15, 25); // returns false
MyCalendar.book(20, 30); // returns true
Explanation: 
The first event can be booked.  The second can't because time 15 is already booked by another event.
The third event can be booked, as the first event takes every time less than 20, but not including 20.

 

Note:

  • The number of calls to MyCalendar.book per test case will be at most 1000.
  • In calls to MyCalendar.book(start, end)start and end are integers in the range [0, 10^9].

 

這道題讓我們設計一個我的日歷類,里面有一個book函數,需要給定一個起始時間和結束時間,與Google Calendar不同的是,我們的事件事件上不能重疊,實際上這道題的本質就是檢查區間是否重疊。那么我們可以暴力搜索,對於每一個將要加入的區間,我們都和已經已經存在的區間進行比較,看是否有重復。而新加入的區間和當前區間產生重復的情況有兩種,一種是新加入區間的前半段重復,並且,另一種是新加入區間的后半段重復。比如當前區間如果是[3, 8),那么第一種情況下新加入區間就是[6, 9),那么觸發條件就是當前區間的起始時間小於等於新加入區間的起始時間,並且結束時間大於新加入區間的結束時間。第二種情況下新加入區間就是[2,5),那么觸發條件就是當前區間的起始時間大於等於新加入區間的起始時間,並且起始時間小於新加入區間的結束時間。這兩種情況均返回false,否則就將新區間加入數組,並返回true即可,參見代碼如下:

 

解法一:

class MyCalendar {
public:
    MyCalendar() {}
    
    bool book(int start, int end) {
        for (auto a : cal) {
            if (a.first <= start && a.second > start) return false;
            if (a.first >= start && a.first < end) return false;
        }
        cal.push_back({start, end});
        return true;
    }

private:
    vector<pair<int, int>> cal;
};

 

下面這種方法將上面方法的兩個if判斷融合成為了一個,我們來觀察兩個區間的起始和結束位置的關系發現,如果兩個區間的起始時間中的較大值小於結束區間的較小值,那么就有重合,返回false。比如 [3, 8) 和 [6, 9),3和6中的較大值6,小於8和9中的較小值8,有重疊。再比如[3, 8) 和 [2, 5),3和2中的較大值3,就小於8和5中的較小值5,有重疊。而對於[3, 8) 和 [9, 10),3和9中的較大值9,不小於8和10中的較小值8,所以沒有重疊,參見代碼如下:

 

解法二:

class MyCalendar {
public:
    MyCalendar() {}
    
    bool book(int start, int end) {
        for (auto a : cal) {
            if (max(a.first, start) < min(a.second, end)) return false;
        }
        cal.push_back({start, end});
        return true;
    }

private:
    vector<pair<int, int>> cal;
};

 

上面兩種解法都是線性搜索,我們起始可以優化搜索時間,如果我們的區間是有序的話。所以我們用一個map來建立起始時間和結束時間的映射,map會按照起始時間進行自動排序。然后對於新進來的區間,我們在已有區間中查找第一個不小於新入區間的起始時間的區間,如果這個區間存在的話,說明新入區間的起始時間小於等於當前區間,也就是解法一中的第二個if情況,當前區間起始時間小於新入區間結束時間的話返回false。我們還要跟前面一個區間進行查重疊操作,那么判斷如果當前區間不是第一個區間的話,就找到前一個區間,此時是解法一中第一個if情況,並且如果前一個區間的結束時間大於新入區間的起始時間的話,返回false。否則就建立新的映射,返回true即可,參見代碼如下:

 

解法三:

class MyCalendar {
public:
    MyCalendar() {}
    
    bool book(int start, int end) {
        auto it = cal.lower_bound(start);
        if (it != cal.end() && it->first < end) return false;
        if (it != cal.begin() && prev(it)->second > start) return false;
        cal[start] = end;
        return true;
    }

private:
    map<int, int> cal;
};

 

參考資料:

https://discuss.leetcode.com/topic/111205/java-8-liner-treemap

https://discuss.leetcode.com/topic/111244/simple-c-o-n-solution 

https://discuss.leetcode.com/topic/111306/clean-c-o-logn-solution

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM