实现一个 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]。
查分数组:
class MyCalendarTwo {
private:
map<int, int> differential_array_book; // 差分数组
public:
MyCalendarTwo() {
}
bool book(int start, int end) {
// 假设成功插入日程安排
// 利用差分数组特性,下面操作相当于将日期[start,end)的日程安排数量+1
differential_array_book[start]++;
differential_array_book[end]--;
int book_num = 0;
for(auto it = differential_array_book.begin(); it != differential_array_book.end();it++) {
book_num += it->second; // 利用差分数组特性计算某日期的日程安排数量
if(book_num >= 3) {
// 如果出现三重预定,回滚日程插入
differential_array_book[start]--;
differential_array_book[end]++;
// 这个操作只是为了节省时间,对正确性没有影响
// 差分数组为0是默认情况,可以删除
if(differential_array_book[start] == 0)
differential_array_book.erase(start);
return false;
}
}
return true;
}
};
线段树:
class MyCalendarTwo {
/**
* 线段树的结点
*/
static class Node {
//左范围
private int leftX;
//右范围
private int rightX;
//最大值
private int max;
//懒标记 0表示不需要操作
private int lazy;
//左子树和右子树
Node leftChild, rightChild;
public Node(int leftX, int rightX) {
this.leftX = leftX;
this.rightX = rightX;
}
}
private Node root;
/**
* 区间更新
*
* @param root 树的根
* @param left 左边界
* @param right 有边界
* @param value 更新值,删除则相当于置为0
*/
public void update(Node root, int left, int right, int value) {
//不在范围内 直接返回
if (root.leftX > right || root.rightX < left) {
return;
}
//修改的区间包含当前结点
if (root.leftX >= left && root.rightX <= right) {
root.max += value;
root.lazy += value;
} else {
//动态开点
lazyCreate(root);
//下传lazy
pushDown(root);
//更新左子树
update(root.leftChild, left, right, value);
//更新右子树
update(root.rightChild, left, right, value);
//上传结果
pushUp(root);
}
}
public int query(Node root, int left, int right) {
if (left <= root.leftX && root.rightX <= right) {
return root.max;
}
lazyCreate(root);
pushDown(root);
int mid = root.leftX + (root.rightX - root.leftX) / 2;
if (right <= mid) {
return query(root.leftChild, left, right);
} else if (left > mid) {
return query(root.rightChild, left, right);
} else {
return Math.max(query(root.leftChild, left, mid), query(root.rightChild, mid + 1, right));
}
}
/**
* 创建左右子树
*
* @param root
*/
public void lazyCreate(Node root) {
if (root.leftChild == null) {
root.leftChild = new Node(root.leftX, root.leftX + (root.rightX - root.leftX) / 2);
}
if (root.rightChild == null) {
root.rightChild = new Node(root.leftX + (root.rightX - root.leftX) / 2 + 1, root.rightX);
}
}
/**
* 下传lazy
*
* @param root
*/
public void pushDown(Node root) {
if (root.lazy > 0) {
int value = root.lazy;
root.leftChild.lazy += value;
root.rightChild.lazy += value;
root.leftChild.max += value;
root.rightChild.max += value;
root.lazy = 0;
}
}
/**
* 上传结果
*
* @param root
*/
public void pushUp(Node root) {
root.max = Math.max(root.leftChild.max, root.rightChild.max);
}
public MyCalendarTwo() {
root = new Node(0, (int) 1e9);
}
public boolean book(int start, int end) {
int query = query(root, start, end - 1);
if (query >= 2) {
return false;
}
update(root, start, end - 1, 1);
return true;
}
}