英文原文
You are implementing a program to use as your calendar. We can add a new event if adding the event will not cause a triple booking.
A triple booking happens when three events have some non-empty intersection (i.e., some moment is common to all the three events.).
The event can be represented as a pair of integers start
and end
that represents a booking on the half-open interval [start, end)
, the range of real numbers x
such that start <= x < end
.
Implement the MyCalendarTwo
class:
MyCalendarTwo()
Initializes the calendar object.boolean book(int start, int end)
Returnstrue
if the event can be added to the calendar successfully without causing a triple booking. Otherwise, returnfalse
and do not add the event to the calendar.
Example 1:
Input ["MyCalendarTwo", "book", "book", "book", "book", "book", "book"] [[], [10, 20], [50, 60], [10, 40], [5, 15], [5, 10], [25, 55]] Output [null, true, true, true, false, true, true] Explanation MyCalendarTwo myCalendarTwo = new MyCalendarTwo(); myCalendarTwo.book(10, 20); // return True, The event can be booked. myCalendarTwo.book(50, 60); // return True, The event can be booked. myCalendarTwo.book(10, 40); // return True, The event can be double booked. myCalendarTwo.book(5, 15); // return False, The event ca not be booked, because it would result in a triple booking. myCalendarTwo.book(5, 10); // return True, The event can be booked, as it does not use time 10 which is already double booked. myCalendarTwo.book(25, 55); // return True, The event 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.
Constraints:
0 <= start < end <= 109
- At most
1000
calls will be made tobook
.
中文题目
实现一个 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)
示例:
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 解释: 前两个日程安排可以添加至日历中。 第三个日程安排会导致双重预订,但可以添加至日历中。 第四个日程安排活动(5,15)不能添加至日历中,因为它会导致三重预订。 第五个日程安排(5,10)可以添加至日历中,因为它未使用已经双重预订的时间10。 第六个日程安排(25,55)可以添加至日历中,因为时间 [25,40] 将和第三个日程安排双重预订; 时间 [40,50] 将单独预订,时间 [50,55)将和第二个日程安排双重预订。
提示:
- 每个测试用例,调用
MyCalendar.book
函数最多不超过1000
次。 - 调用函数
MyCalendar.book(start, end)
时,start
和end
的取值范围为[0, 10^9]
。
通过代码
官方题解
方法一:暴力法
维护一重预订列表和双重预订列表。当预订一个新的日程安排 [start, end)
时,如果它与双重预订列表冲突,则会产生三重预定。
算法:
- 当且仅当事件
[s1, e1)
在另一个事件[s2, e2)
结束后开始,两个事件不冲突,也就是说满足e1<=s2
或e2<=s1
时,两个事件不冲突。这意味着当s1<e2
和s2<e1
时,事件发生冲突。 - 如果新的日程安排与双重预订冲突,则返回
false
。否则,我们会将与一重预定列表冲突的时间添加到双重预订列表中,并将该预定添加到一重预定列表中。
class MyCalendarTwo:
def __init__(self):
self.calendar = []
self.overlaps = []
def book(self, start, end):
for i, j in self.overlaps:
if start < j and end > i:
return False
for i, j in self.calendar:
if start < j and end > i:
self.overlaps.append((max(start, i), min(end, j)))
self.calendar.append((start, end))
return True
public class MyCalendarTwo {
List<int[]> calendar;
List<int[]> overlaps;
MyCalendarTwo() {
calendar = new ArrayList();
}
public boolean book(int start, int end) {
for (int[] iv: overlaps) {
if (iv[0] < end && start < iv[1]) return false;
}
for (int[] iv: calendar) {
if (iv[0] < end && start < iv[1])
overlaps.add(new int[]{Math.max(start, iv[0]), Math.min(end, iv[1])});
}
calendar.add(new int[]{start, end});
return true;
}
}
复杂度分析
- 时间复杂度:$O(N^2)$。$N$ 指的是日程安排的数量。对于每个新的日程安排,我们会遍历已预定的每个日程安排,以决定是否可以预订新的日程安排。这需要 $\sum_k^N O(k) = O(N^2)$ 的时间。
- 空间复杂度:$O(N)$,
calendar
所使用的空间。
方法二:边界计数
算法:
- 当我们预定一个新的日程安排
[start, end)
,则执行delta[start]++
和delta[end]--
。其中delta
是按照key
值从小到大排序的结构,我们用active
来记录当前正在进行的日程安排数,当active>=3
时,说明产生了三重预定。 - 此方法不包括 python 实现,因为没有 TreeMap。
class MyCalendarTwo { TreeMap<Integer, Integer> delta; public MyCalendarTwo() { delta = new TreeMap(); } public boolean book(int start, int end) { delta.put(start, delta.getOrDefault(start, 0) + 1); delta.put(end, delta.getOrDefault(end, 0) - 1); int active = 0; for (int d: delta.values()) { active += d; if (active >= 3) { delta.put(start, delta.get(start) - 1); delta.put(end, delta.get(end) + 1); if (delta.get(start) == 0) delta.remove(start); return false; } } return true; } }
复杂度分析
- 时间复杂度:$O(N^2)$。$N$ 指的是日程安排的数量。对于每个新的日程安排,我们遍历
delta
需要 $O(N)$ 的时间 - 空间复杂度:$O(N)$,
delta
所使用的空间。
统计信息
通过次数 | 提交次数 | AC比率 |
---|---|---|
4414 | 8754 | 50.4% |
提交历史
提交时间 | 提交结果 | 执行时间 | 内存消耗 | 语言 |
---|
相似题目
题目 | 难度 |
---|---|
我的日程安排表 I | 中等 |
我的日程安排表 III | 困难 |