Implement a MyCalendarTwo
class to store your events. A new event can be added if adding the event will not cause a triple booking.html
Your class will have one 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
.git
A triple booking happens when three events have some non-empty intersection (ie., there is some time that is common to all 3 events.)github
For each call to the method MyCalendar.book
, return true
if the event can be added to the calendar successfully without causing a triple booking. Otherwise, return false
and do not add the event to the calendar.app
Your class will be called like this: MyCalendar cal = new MyCalendar();
MyCalendar.book(start, end)
post
Example 1:this
MyCalendar(); MyCalendar.book(10, 20); // returns true MyCalendar.book(50, 60); // returns true MyCalendar.book(10, 40); // returns true MyCalendar.book(5, 15); // returns false MyCalendar.book(5, 10); // returns true MyCalendar.book(25, 55); // returns true Explanation: The first two events can be booked. The third event can be double booked. The fourth event (5, 15) can't be booked, because it would result in a triple booking. The fifth event (5, 10) can be booked, as it does not use time 10 which is already double booked. The sixth event (25, 55) can be booked, as the time in [25, 40) will be double booked with the third event; the time [40, 50) will be single booked, and the time [50, 55) will be double booked with the second event.
Note:url
MyCalendar.book
per test case will be at most 1000
.MyCalendar.book(start, end)
, start
and end
are integers in the range [0, 10^9]
.
這道題是 My Calendar I 的拓展,以前那道題說是不能有任何的重疊區間,而這道題說最多容忍兩個重疊區域,注意是重疊區域,不是事件。好比事件 A,B,C 互不重疊,可是有一個事件D,和這三個事件都重疊,這樣是能夠的,由於重疊的區域最多隻有兩個。因此關鍵仍是要知道具體的重疊區域,若是兩個事件重疊,那麼重疊區域就是它們的交集,求交集的方法是兩個區間的起始時間中的較大值,到結束時間中的較小值。能夠用一個 TreeSet 來專門存重疊區間,再用一個 TreeSet 來存完整的區間,那麼思路就是,先遍歷專門存重疊區間的 TreeSet,由於能在這裏出現的區間,都已是出現兩次了,若是當前新的區間跟重疊區間有交集的話,說明此時三個事件重疊了,直接返回 false。若是當前區間跟重疊區間沒有交集的話,則再來遍歷完整區間的集合,若是有交集的話,那麼應該算出重疊區間而且加入放重疊區間的 TreeSet 中。最後記得將新區間加入完整區間的 TreeSet 中,參見代碼以下:spa
解法一:code
class MyCalendarTwo { public: MyCalendarTwo() {} bool book(int start, int end) { for (auto &a : s2) { if (start >= a.second || end <= a.first) continue; return false; } for (auto &a : s1) { if (start >= a.second || end <= a.first) continue; s2.insert({max(start, a.first), min(end, a.second)}); } s1.insert({start, end}); return true; } private: set<pair<int, int>> s1, s2; };
下面這種方法至關的巧妙,創建一個時間點和次數之間的映射,規定遇到起始時間點,次數加1,遇到結束時間點,次數減1。那麼首先更改新的起始時間 start 和結束時間 end 的映射,start 對應值增1,end 對應值減1。而後定義一個變量 cnt,來統計當前的次數。使用 TreeMap 具備自動排序的功能,因此遍歷的時候就是按時間順序的,最早遍歷到的必定是一個起始時間,因此加上其映射值,必定是個正數。若是此時只有一個區間,就是剛加進來的區間的話,那麼首先確定遍歷到 start,那麼 cnt 此時加1,而後就會遍歷到 end,那麼此時 cnt 減1,最後下來 cnt 爲0,沒有重疊。仍是用具體數字來講吧,如今假設 TreeMap 中已經加入了一個區間 [3, 5) 了,就有下面的映射:orm
3 -> 1
5 -> -1
假如此時要加入的區間爲 [3, 8) 的話,則先對3和8分別加1減1,此時的映射爲:
3 -> 2
5 -> -1
8 -> -1
最早遍歷到3,cnt 爲2,沒有超過3,此時有兩個事件有重疊,是容許的。而後遍歷5和8,分別減去1,最終又變成0了,始終 cnt 沒有超過2,因此是符合題意的。若是此時再加入一個新的區間 [1, 4),則先對1和4分別加1減1,那麼此時的映射爲:
1 -> 1
3 -> 2
4 -> -1
5 -> -1
8 -> -1
先遍歷到1,cnt爲1,而後遍歷到3,此時 cnt 爲3了,那麼就知道有三個事件有重疊區間了,因此這個新區間是不能加入的,須要還原其 start 和 end 作的操做,把 start 的映射值減1,end 的映射值加1,而後返回 false。不然沒有三個事件有共同重疊區間的話,返回 true 便可,參見代碼以下:
解法二:
class MyCalendarTwo { public: MyCalendarTwo() {} bool book(int start, int end) { ++freq[start]; --freq[end]; int cnt = 0; for (auto f : freq) { cnt += f.second; if (cnt == 3) { --freq[start]; ++freq[end]; return false; } } return true; } private: map<int, int> freq; };
Github 同步地址:
https://github.com/grandyang/leetcode/issues/731
相似題目:
參考資料:
https://leetcode.com/problems/my-calendar-ii/
https://leetcode.com/problems/my-calendar-ii/discuss/109550/Simple-AC-by-TreeMap
https://leetcode.com/problems/my-calendar-ii/discuss/109522/Simplified-winner's-solution
https://leetcode.com/problems/my-calendar-ii/discuss/109519/JavaC%2B%2B-Clean-Code-with-Explanation