diff --git a/solution/0700-0799/0731.My Calendar II/README.md b/solution/0700-0799/0731.My Calendar II/README.md index 2261e15a63f42..a6a10f70138c4 100644 --- a/solution/0700-0799/0731.My Calendar II/README.md +++ b/solution/0700-0799/0731.My Calendar II/README.md @@ -72,9 +72,9 @@ myCalendarTwo.book(25, 55); // 返回 True,能够预定该日程,因为时 ### 方法一:差分 -利用有序哈希表实现。 +我们可以利用差分的思想,将每个时间点的预定情况记录下来,然后遍历所有时间点,统计当前时间点的预定情况,如果预定次数超过 $2$ 次,则返回 $\textit{false}$。否则,返回 $\textit{true}$。 -时间复杂度 $O(n^2)$,其中 $n$ 表示日程安排的数量。 +时间复杂度 $O(n^2)$,空间复杂度 $O(n)$,其中 $n$ 表示日程安排的数量。 @@ -85,45 +85,46 @@ from sortedcontainers import SortedDict class MyCalendarTwo: + def __init__(self): self.sd = SortedDict() - def book(self, start: int, end: int) -> bool: - self.sd[start] = self.sd.get(start, 0) + 1 - self.sd[end] = self.sd.get(end, 0) - 1 + def book(self, startTime: int, endTime: int) -> bool: + self.sd[startTime] = self.sd.get(startTime, 0) + 1 + self.sd[endTime] = self.sd.get(endTime, 0) - 1 s = 0 for v in self.sd.values(): s += v if s > 2: - self.sd[start] -= 1 - self.sd[end] += 1 + self.sd[startTime] -= 1 + self.sd[endTime] += 1 return False return True # Your MyCalendarTwo object will be instantiated and called as such: # obj = MyCalendarTwo() -# param_1 = obj.book(start,end) +# param_1 = obj.book(startTime,endTime) ``` #### Java ```java class MyCalendarTwo { - private Map tm = new TreeMap<>(); + private final Map tm = new TreeMap<>(); public MyCalendarTwo() { } - public boolean book(int start, int end) { - tm.put(start, tm.getOrDefault(start, 0) + 1); - tm.put(end, tm.getOrDefault(end, 0) - 1); + public boolean book(int startTime, int endTime) { + tm.merge(startTime, 1, Integer::sum); + tm.merge(endTime, -1, Integer::sum); int s = 0; for (int v : tm.values()) { s += v; if (s > 2) { - tm.put(start, tm.get(start) - 1); - tm.put(end, tm.get(end) + 1); + tm.merge(startTime, -1, Integer::sum); + tm.merge(endTime, 1, Integer::sum); return false; } } @@ -134,7 +135,7 @@ class MyCalendarTwo { /** * Your MyCalendarTwo object will be instantiated and called as such: * MyCalendarTwo obj = new MyCalendarTwo(); - * boolean param_1 = obj.book(start,end); + * boolean param_1 = obj.book(startTime,endTime); */ ``` @@ -143,31 +144,32 @@ class MyCalendarTwo { ```cpp class MyCalendarTwo { public: - map m; - MyCalendarTwo() { } - bool book(int start, int end) { - ++m[start]; - --m[end]; + bool book(int startTime, int endTime) { + ++m[startTime]; + --m[endTime]; int s = 0; for (auto& [_, v] : m) { s += v; if (s > 2) { - --m[start]; - ++m[end]; + --m[startTime]; + ++m[endTime]; return false; } } return true; } + +private: + map m; }; /** * Your MyCalendarTwo object will be instantiated and called as such: * MyCalendarTwo* obj = new MyCalendarTwo(); - * bool param_1 = obj->book(start,end); + * bool param_1 = obj->book(startTime,endTime); */ ``` @@ -175,30 +177,32 @@ public: ```go type MyCalendarTwo struct { - *redblacktree.Tree + rbt *redblacktree.Tree[int, int] } func Constructor() MyCalendarTwo { - return MyCalendarTwo{redblacktree.NewWithIntComparator()} + return MyCalendarTwo{rbt: redblacktree.New[int, int]()} } -func (this *MyCalendarTwo) Book(start int, end int) bool { - add := func(key, val int) { - if v, ok := this.Get(key); ok { - this.Put(key, v.(int)+val) +func (this *MyCalendarTwo) Book(startTime int, endTime int) bool { + merge := func(x, v int) { + c, _ := this.rbt.Get(x) + if c+v == 0 { + this.rbt.Remove(x) } else { - this.Put(key, val) + this.rbt.Put(x, c+v) } } - add(start, 1) - add(end, -1) + + merge(startTime, 1) + merge(endTime, -1) + s := 0 - it := this.Iterator() - for it.Next() { - s += it.Value().(int) + for _, v := range this.rbt.Values() { + s += v if s > 2 { - add(start, -1) - add(end, 1) + merge(startTime, -1) + merge(endTime, 1) return false } } @@ -208,7 +212,7 @@ func (this *MyCalendarTwo) Book(start int, end int) bool { /** * Your MyCalendarTwo object will be instantiated and called as such: * obj := Constructor(); - * param_1 := obj.Book(start,end); + * param_1 := obj.Book(startTime,endTime); */ ``` @@ -216,28 +220,26 @@ func (this *MyCalendarTwo) Book(start int, end int) bool { ```ts class MyCalendarTwo { - private events: [number, number][]; - private overlaps: [number, number][]; + private tm: Record = {}; - constructor() { - this.events = []; - this.overlaps = []; - } + constructor() {} - book(start: number, end: number): boolean { - for (const [s, e] of this.overlaps) { - if (Math.max(start, s) < Math.min(end, e)) { + book(startTime: number, endTime: number): boolean { + this.tm[startTime] = (this.tm[startTime] ?? 0) + 1; + this.tm[endTime] = (this.tm[endTime] ?? 0) - 1; + let s = 0; + for (const v of Object.values(this.tm)) { + s += v; + if (s > 2) { + if (--this.tm[startTime] === 0) { + delete this.tm[startTime]; + } + if (++this.tm[endTime] === 0) { + delete this.tm[endTime]; + } return false; } } - - for (const [s, e] of this.events) { - if (Math.max(start, s) < Math.min(end, e)) { - this.overlaps.push([Math.max(start, s), Math.min(end, e)]); - } - } - - this.events.push([start, end]); return true; } } @@ -245,7 +247,7 @@ class MyCalendarTwo { /** * Your MyCalendarTwo object will be instantiated and called as such: * var obj = new MyCalendarTwo() - * var param_1 = obj.book(start,end) + * var param_1 = obj.book(startTime,endTime) */ ``` @@ -253,36 +255,38 @@ class MyCalendarTwo { ```js var MyCalendarTwo = function () { - this.events = []; - this.overlaps = []; + this.tm = {}; }; /** - * @param {number} start - * @param {number} end + * @param {number} startTime + * @param {number} endTime * @return {boolean} */ -MyCalendarTwo.prototype.book = function (start, end) { - for (let [s, e] of this.overlaps) { - if (Math.max(start, s) < Math.min(end, e)) { +MyCalendarTwo.prototype.book = function (startTime, endTime) { + this.tm[startTime] = (this.tm[startTime] || 0) + 1; + this.tm[endTime] = (this.tm[endTime] || 0) - 1; + let s = 0; + + for (const v of Object.values(this.tm)) { + s += v; + if (s > 2) { + if (--this.tm[startTime] === 0) { + delete this.tm[startTime]; + } + if (++this.tm[endTime] === 0) { + delete this.tm[endTime]; + } return false; } } - - for (let [s, e] of this.events) { - if (Math.max(start, s) < Math.min(end, e)) { - this.overlaps.push([Math.max(start, s), Math.min(end, e)]); - } - } - - this.events.push([start, end]); return true; }; /** * Your MyCalendarTwo object will be instantiated and called as such: * var obj = new MyCalendarTwo() - * var param_1 = obj.book(start,end) + * var param_1 = obj.book(startTime,endTime) */ ``` @@ -294,21 +298,21 @@ MyCalendarTwo.prototype.book = function (start, end) { ### 方法二:线段树 -线段树将整个区间分割为多个不连续的子区间,子区间的数量不超过 $log(width)$。更新某个元素的值,只需要更新 $log(width)$ 个区间,并且这些区间都包含在一个包含该元素的大区间内。区间修改时,需要使用**懒标记**保证效率。 +线段树将整个区间分割为多个不连续的子区间,子区间的数量不超过 $\log(\textit{width})$。更新某个元素的值,只需要更新 $\log(\textit{width})$ 个区间,并且这些区间都包含在一个包含该元素的大区间内。区间修改时,需要使用**懒标记**保证效率。 - 线段树的每个节点代表一个区间; - 线段树具有唯一的根节点,代表的区间是整个统计范围,如 $[1,N]$; - 线段树的每个叶子节点代表一个长度为 $1$ 的元区间 $[x, x]$; -- 对于每个内部节点 $[l,r]$,它的左儿子是 $[l,mid]$,右儿子是 $[mid+1,r]$, 其中 $mid = ⌊(l+r)/2⌋$ (即向下取整)。 +- 对于每个内部节点 $[l,r]$,它的左儿子是 $[l,\textit{mid}]$,右儿子是 $[\textit{mid}+1,r]$, 其中 $\textit{mid} = ⌊(l+r)/2⌋$ (即向下取整)。 对于本题,线段树节点维护的信息有: 1. 区间范围内被预定的次数的最大值 $v$ -1. 懒标记 $add$ +1. 懒标记 $\textit{add}$ 由于时间范围为 $10^9$,非常大,因此我们采用动态开点。 -时间复杂度 $O(nlogn)$,其中 $n$ 表示日程安排的数量。 +时间复杂度 $O(n \times log n)$,空间复杂度 $O(n)$,其中 $n$ 表示日程安排的数量。 @@ -316,7 +320,7 @@ MyCalendarTwo.prototype.book = function (start, end) { ```python class Node: - def __init__(self, l, r): + def __init__(self, l: int, r: int): self.left = None self.right = None self.l = l @@ -328,9 +332,9 @@ class Node: class SegmentTree: def __init__(self): - self.root = Node(1, int(1e9 + 1)) + self.root = Node(1, 10**9 + 1) - def modify(self, l, r, v, node=None): + def modify(self, l: int, r: int, v: int, node: Node = None): if l > r: return if node is None: @@ -346,7 +350,7 @@ class SegmentTree: self.modify(l, r, v, node.right) self.pushup(node) - def query(self, l, r, node=None): + def query(self, l: int, r: int, node: Node = None) -> int: if l > r: return 0 if node is None: @@ -361,10 +365,10 @@ class SegmentTree: v = max(v, self.query(l, r, node.right)) return v - def pushup(self, node): + def pushup(self, node: Node): node.v = max(node.left.v, node.right.v) - def pushdown(self, node): + def pushdown(self, node: Node): if node.left is None: node.left = Node(node.l, node.mid) if node.right is None: @@ -381,16 +385,16 @@ class MyCalendarTwo: def __init__(self): self.tree = SegmentTree() - def book(self, start: int, end: int) -> bool: - if self.tree.query(start + 1, end) >= 2: + def book(self, startTime: int, endTime: int) -> bool: + if self.tree.query(startTime + 1, endTime) >= 2: return False - self.tree.modify(start + 1, end, 1) + self.tree.modify(startTime + 1, endTime, 1) return True # Your MyCalendarTwo object will be instantiated and called as such: # obj = MyCalendarTwo() -# param_1 = obj.book(start,end) +# param_1 = obj.book(startTime,endTime) ``` #### Java @@ -490,11 +494,11 @@ class MyCalendarTwo { public MyCalendarTwo() { } - public boolean book(int start, int end) { - if (tree.query(start + 1, end) >= 2) { + public boolean book(int startTime, int endTime) { + if (tree.query(startTime + 1, endTime) >= 2) { return false; } - tree.modify(start + 1, end, 1); + tree.modify(startTime + 1, endTime, 1); return true; } } @@ -502,7 +506,7 @@ class MyCalendarTwo { /** * Your MyCalendarTwo object will be instantiated and called as such: * MyCalendarTwo obj = new MyCalendarTwo(); - * boolean param_1 = obj.book(start,end); + * boolean param_1 = obj.book(startTime,endTime); */ ``` @@ -511,77 +515,90 @@ class MyCalendarTwo { ```cpp class Node { public: + int l, r, mid, v, add; Node* left; Node* right; - int l; - int r; - int mid; - int v; - int add; - Node(int l, int r) { - this->l = l; - this->r = r; - this->mid = (l + r) >> 1; - this->left = this->right = nullptr; - v = add = 0; - } + Node(int l, int r) + : l(l) + , r(r) + , mid((l + r) >> 1) + , v(0) + , add(0) + , left(nullptr) + , right(nullptr) {} }; class SegmentTree { -private: +public: Node* root; -public: SegmentTree() { root = new Node(1, 1e9 + 1); } - void modify(int l, int r, int v) { - modify(l, r, v, root); - } + void modify(int l, int r, int v, Node* node = nullptr) { + if (l > r) { + return; + } + if (node == nullptr) { + node = root; + } - void modify(int l, int r, int v, Node* node) { - if (l > r) return; if (node->l >= l && node->r <= r) { node->v += v; node->add += v; return; } pushdown(node); - if (l <= node->mid) modify(l, r, v, node->left); - if (r > node->mid) modify(l, r, v, node->right); + if (l <= node->mid) { + modify(l, r, v, node->left); + } + if (r > node->mid) { + modify(l, r, v, node->right); + } pushup(node); } - int query(int l, int r) { - return query(l, r, root); - } + int query(int l, int r, Node* node = nullptr) { + if (l > r) { + return 0; + } + if (node == nullptr) { + node = root; + } - int query(int l, int r, Node* node) { - if (l > r) return 0; - if (node->l >= l && node->r <= r) return node->v; + if (node->l >= l && node->r <= r) { + return node->v; + } pushdown(node); int v = 0; - if (l <= node->mid) v = max(v, query(l, r, node->left)); - if (r > node->mid) v = max(v, query(l, r, node->right)); + if (l <= node->mid) { + v = max(v, query(l, r, node->left)); + } + if (r > node->mid) { + v = max(v, query(l, r, node->right)); + } return v; } +private: void pushup(Node* node) { node->v = max(node->left->v, node->right->v); } void pushdown(Node* node) { - if (!node->left) node->left = new Node(node->l, node->mid); - if (!node->right) node->right = new Node(node->mid + 1, node->r); + if (node->left == nullptr) { + node->left = new Node(node->l, node->mid); + } + if (node->right == nullptr) { + node->right = new Node(node->mid + 1, node->r); + } if (node->add) { - Node* left = node->left; - Node* right = node->right; - left->v += node->add; - right->v += node->add; - left->add += node->add; - right->add += node->add; + node->left->v += node->add; + node->right->v += node->add; + node->left->add += node->add; + node->right->add += node->add; node->add = 0; } } @@ -589,14 +606,15 @@ public: class MyCalendarTwo { public: - SegmentTree* tree = new SegmentTree(); + SegmentTree tree; - MyCalendarTwo() { - } + MyCalendarTwo() {} - bool book(int start, int end) { - if (tree->query(start + 1, end) >= 2) return false; - tree->modify(start + 1, end, 1); + bool book(int startTime, int endTime) { + if (tree.query(startTime + 1, endTime) >= 2) { + return false; + } + tree.modify(startTime + 1, endTime, 1); return true; } }; @@ -604,7 +622,7 @@ public: /** * Your MyCalendarTwo object will be instantiated and called as such: * MyCalendarTwo* obj = new MyCalendarTwo(); - * bool param_1 = obj->book(start,end); + * bool param_1 = obj->book(startTime,endTime); */ ``` @@ -701,89 +719,230 @@ func Constructor() MyCalendarTwo { return MyCalendarTwo{newSegmentTree()} } -func (this *MyCalendarTwo) Book(start int, end int) bool { - if this.tree.query(start+1, end, this.tree.root) >= 2 { +func (this *MyCalendarTwo) Book(startTime int, endTime int) bool { + if this.tree.query(startTime+1, endTime, this.tree.root) >= 2 { return false } - this.tree.modify(start+1, end, 1, this.tree.root) + this.tree.modify(startTime+1, endTime, 1, this.tree.root) return true } /** * Your MyCalendarTwo object will be instantiated and called as such: * obj := Constructor(); - * param_1 := obj.Book(start,end); + * param_1 := obj.Book(startTime,endTime); */ ``` - - - +#### TypeScript - +```ts +class Node { + left: Node | null = null; + right: Node | null = null; + l: number; + r: number; + mid: number; + v: number = 0; + add: number = 0; + + constructor(l: number, r: number) { + this.l = l; + this.r = r; + this.mid = (l + r) >> 1; + } +} -### Solution 3: Line Sweep +class SegmentTree { + private root: Node = new Node(1, 1e9 + 1); - + modify(l: number, r: number, v: number, node: Node | null = this.root): void { + if (l > r || !node) { + return; + } + if (node.l >= l && node.r <= r) { + node.v += v; + node.add += v; + return; + } + this.pushdown(node); + if (l <= node.mid) { + this.modify(l, r, v, node.left); + } + if (r > node.mid) { + this.modify(l, r, v, node.right); + } + this.pushup(node); + } -#### TypeScript + query(l: number, r: number, node: Node | null = this.root): number { + if (l > r || !node) { + return 0; + } + if (node.l >= l && node.r <= r) { + return node.v; + } + this.pushdown(node); + let v = 0; + if (l <= node.mid) { + v = Math.max(v, this.query(l, r, node.left)); + } + if (r > node.mid) { + v = Math.max(v, this.query(l, r, node.right)); + } + return v; + } -```ts -class MyCalendarTwo { - #OVERLAPS = 2; - #cnt: Record = {}; + private pushup(node: Node): void { + if (node.left && node.right) { + node.v = Math.max(node.left.v, node.right.v); + } + } - book(start: number, end: number): boolean { - this.#cnt[start] = (this.#cnt[start] ?? 0) + 1; - this.#cnt[end] = (this.#cnt[end] ?? 0) - 1; + private pushdown(node: Node): void { + if (!node.left) { + node.left = new Node(node.l, node.mid); + } + if (!node.right) { + node.right = new Node(node.mid + 1, node.r); + } + if (node.add) { + let left = node.left; + let right = node.right; + left.add += node.add; + right.add += node.add; + left.v += node.add; + right.v += node.add; + node.add = 0; + } + } +} - let sum = 0; - for (const v of Object.values(this.#cnt)) { - sum += v; - if (sum > this.#OVERLAPS) { - this.#cnt[start]--; - this.#cnt[end]++; +class MyCalendarTwo { + private tree: SegmentTree = new SegmentTree(); - if (!this.#cnt[start]) delete this.#cnt[start]; - if (!this.#cnt[end]) delete this.#cnt[end]; + constructor() {} - return false; - } + book(startTime: number, endTime: number): boolean { + if (this.tree.query(startTime + 1, endTime) >= 2) { + return false; } - + this.tree.modify(startTime + 1, endTime, 1); return true; } } + +/** + * Your MyCalendarTwo object will be instantiated and called as such: + * var obj = new MyCalendarTwo() + * var param_1 = obj.book(startTime,endTime) + */ ``` #### JavaScript ```js -class MyCalendarTwo { - #OVERLAPS = 2; - #cnt = {}; +class Node { + constructor(l, r) { + this.left = null; + this.right = null; + this.l = l; + this.r = r; + this.mid = (l + r) >> 1; + this.v = 0; + this.add = 0; + } +} - book(start, end) { - this.#cnt[start] = (this.#cnt[start] ?? 0) + 1; - this.#cnt[end] = (this.#cnt[end] ?? 0) - 1; +class SegmentTree { + constructor() { + this.root = new Node(1, 1e9 + 1); + } - let sum = 0; - for (const v of Object.values(this.#cnt)) { - sum += v; - if (sum > this.#OVERLAPS) { - this.#cnt[start]--; - this.#cnt[end]++; + modify(l, r, v, node = this.root) { + if (l > r || !node) { + return; + } + if (node.l >= l && node.r <= r) { + node.v += v; + node.add += v; + return; + } + this.pushdown(node); + if (l <= node.mid) { + this.modify(l, r, v, node.left); + } + if (r > node.mid) { + this.modify(l, r, v, node.right); + } + this.pushup(node); + } - if (!this.#cnt[start]) delete this.#cnt[start]; - if (!this.#cnt[end]) delete this.#cnt[end]; + query(l, r, node = this.root) { + if (l > r || !node) { + return 0; + } + if (node.l >= l && node.r <= r) { + return node.v; + } + this.pushdown(node); + let v = 0; + if (l <= node.mid) { + v = Math.max(v, this.query(l, r, node.left)); + } + if (r > node.mid) { + v = Math.max(v, this.query(l, r, node.right)); + } + return v; + } - return false; - } + pushup(node) { + if (node.left && node.right) { + node.v = Math.max(node.left.v, node.right.v); } + } - return true; + pushdown(node) { + if (!node.left) { + node.left = new Node(node.l, node.mid); + } + if (!node.right) { + node.right = new Node(node.mid + 1, node.r); + } + if (node.add) { + const left = node.left; + const right = node.right; + left.add += node.add; + right.add += node.add; + left.v += node.add; + right.v += node.add; + node.add = 0; + } } } + +var MyCalendarTwo = function () { + this.tree = new SegmentTree(); +}; + +/** + * @param {number} startTime + * @param {number} endTime + * @return {boolean} + */ +MyCalendarTwo.prototype.book = function (startTime, endTime) { + if (this.tree.query(startTime + 1, endTime) >= 2) { + return false; + } + this.tree.modify(startTime + 1, endTime, 1); + return true; +}; + +/** + * Your MyCalendarTwo object will be instantiated and called as such: + * var obj = new MyCalendarTwo() + * var param_1 = obj.book(startTime,endTime) + */ ``` diff --git a/solution/0700-0799/0731.My Calendar II/README_EN.md b/solution/0700-0799/0731.My Calendar II/README_EN.md index 178bc2f4af6cf..1c5a597a94a56 100644 --- a/solution/0700-0799/0731.My Calendar II/README_EN.md +++ b/solution/0700-0799/0731.My Calendar II/README_EN.md @@ -46,9 +46,9 @@ tags: 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(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 cannot 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. @@ -68,7 +68,11 @@ myCalendarTwo.book(25, 55); // return True, The event can be booked, as the time -### Solution 1 +### Solution 1: Difference Array + +We can use the concept of a difference array to record the booking status at each time point. Then, we traverse all the time points and count the booking status at the current time point. If the number of bookings exceeds $2$, we return $\textit{false}$. Otherwise, we return $\textit{true}$. + +The time complexity is $O(n^2)$, and the space complexity is $O(n)$, where $n$ is the number of bookings. @@ -79,45 +83,46 @@ from sortedcontainers import SortedDict class MyCalendarTwo: + def __init__(self): self.sd = SortedDict() - def book(self, start: int, end: int) -> bool: - self.sd[start] = self.sd.get(start, 0) + 1 - self.sd[end] = self.sd.get(end, 0) - 1 + def book(self, startTime: int, endTime: int) -> bool: + self.sd[startTime] = self.sd.get(startTime, 0) + 1 + self.sd[endTime] = self.sd.get(endTime, 0) - 1 s = 0 for v in self.sd.values(): s += v if s > 2: - self.sd[start] -= 1 - self.sd[end] += 1 + self.sd[startTime] -= 1 + self.sd[endTime] += 1 return False return True # Your MyCalendarTwo object will be instantiated and called as such: # obj = MyCalendarTwo() -# param_1 = obj.book(start,end) +# param_1 = obj.book(startTime,endTime) ``` #### Java ```java class MyCalendarTwo { - private Map tm = new TreeMap<>(); + private final Map tm = new TreeMap<>(); public MyCalendarTwo() { } - public boolean book(int start, int end) { - tm.put(start, tm.getOrDefault(start, 0) + 1); - tm.put(end, tm.getOrDefault(end, 0) - 1); + public boolean book(int startTime, int endTime) { + tm.merge(startTime, 1, Integer::sum); + tm.merge(endTime, -1, Integer::sum); int s = 0; for (int v : tm.values()) { s += v; if (s > 2) { - tm.put(start, tm.get(start) - 1); - tm.put(end, tm.get(end) + 1); + tm.merge(startTime, -1, Integer::sum); + tm.merge(endTime, 1, Integer::sum); return false; } } @@ -128,7 +133,7 @@ class MyCalendarTwo { /** * Your MyCalendarTwo object will be instantiated and called as such: * MyCalendarTwo obj = new MyCalendarTwo(); - * boolean param_1 = obj.book(start,end); + * boolean param_1 = obj.book(startTime,endTime); */ ``` @@ -137,31 +142,32 @@ class MyCalendarTwo { ```cpp class MyCalendarTwo { public: - map m; - MyCalendarTwo() { } - bool book(int start, int end) { - ++m[start]; - --m[end]; + bool book(int startTime, int endTime) { + ++m[startTime]; + --m[endTime]; int s = 0; for (auto& [_, v] : m) { s += v; if (s > 2) { - --m[start]; - ++m[end]; + --m[startTime]; + ++m[endTime]; return false; } } return true; } + +private: + map m; }; /** * Your MyCalendarTwo object will be instantiated and called as such: * MyCalendarTwo* obj = new MyCalendarTwo(); - * bool param_1 = obj->book(start,end); + * bool param_1 = obj->book(startTime,endTime); */ ``` @@ -169,30 +175,32 @@ public: ```go type MyCalendarTwo struct { - *redblacktree.Tree + rbt *redblacktree.Tree[int, int] } func Constructor() MyCalendarTwo { - return MyCalendarTwo{redblacktree.NewWithIntComparator()} + return MyCalendarTwo{rbt: redblacktree.New[int, int]()} } -func (this *MyCalendarTwo) Book(start int, end int) bool { - add := func(key, val int) { - if v, ok := this.Get(key); ok { - this.Put(key, v.(int)+val) +func (this *MyCalendarTwo) Book(startTime int, endTime int) bool { + merge := func(x, v int) { + c, _ := this.rbt.Get(x) + if c+v == 0 { + this.rbt.Remove(x) } else { - this.Put(key, val) + this.rbt.Put(x, c+v) } } - add(start, 1) - add(end, -1) + + merge(startTime, 1) + merge(endTime, -1) + s := 0 - it := this.Iterator() - for it.Next() { - s += it.Value().(int) + for _, v := range this.rbt.Values() { + s += v if s > 2 { - add(start, -1) - add(end, 1) + merge(startTime, -1) + merge(endTime, 1) return false } } @@ -202,7 +210,7 @@ func (this *MyCalendarTwo) Book(start int, end int) bool { /** * Your MyCalendarTwo object will be instantiated and called as such: * obj := Constructor(); - * param_1 := obj.Book(start,end); + * param_1 := obj.Book(startTime,endTime); */ ``` @@ -210,28 +218,26 @@ func (this *MyCalendarTwo) Book(start int, end int) bool { ```ts class MyCalendarTwo { - private events: [number, number][]; - private overlaps: [number, number][]; + private tm: Record = {}; - constructor() { - this.events = []; - this.overlaps = []; - } + constructor() {} - book(start: number, end: number): boolean { - for (const [s, e] of this.overlaps) { - if (Math.max(start, s) < Math.min(end, e)) { + book(startTime: number, endTime: number): boolean { + this.tm[startTime] = (this.tm[startTime] ?? 0) + 1; + this.tm[endTime] = (this.tm[endTime] ?? 0) - 1; + let s = 0; + for (const v of Object.values(this.tm)) { + s += v; + if (s > 2) { + if (--this.tm[startTime] === 0) { + delete this.tm[startTime]; + } + if (++this.tm[endTime] === 0) { + delete this.tm[endTime]; + } return false; } } - - for (const [s, e] of this.events) { - if (Math.max(start, s) < Math.min(end, e)) { - this.overlaps.push([Math.max(start, s), Math.min(end, e)]); - } - } - - this.events.push([start, end]); return true; } } @@ -239,7 +245,7 @@ class MyCalendarTwo { /** * Your MyCalendarTwo object will be instantiated and called as such: * var obj = new MyCalendarTwo() - * var param_1 = obj.book(start,end) + * var param_1 = obj.book(startTime,endTime) */ ``` @@ -247,36 +253,38 @@ class MyCalendarTwo { ```js var MyCalendarTwo = function () { - this.events = []; - this.overlaps = []; + this.tm = {}; }; /** - * @param {number} start - * @param {number} end + * @param {number} startTime + * @param {number} endTime * @return {boolean} */ -MyCalendarTwo.prototype.book = function (start, end) { - for (let [s, e] of this.overlaps) { - if (Math.max(start, s) < Math.min(end, e)) { +MyCalendarTwo.prototype.book = function (startTime, endTime) { + this.tm[startTime] = (this.tm[startTime] || 0) + 1; + this.tm[endTime] = (this.tm[endTime] || 0) - 1; + let s = 0; + + for (const v of Object.values(this.tm)) { + s += v; + if (s > 2) { + if (--this.tm[startTime] === 0) { + delete this.tm[startTime]; + } + if (++this.tm[endTime] === 0) { + delete this.tm[endTime]; + } return false; } } - - for (let [s, e] of this.events) { - if (Math.max(start, s) < Math.min(end, e)) { - this.overlaps.push([Math.max(start, s), Math.min(end, e)]); - } - } - - this.events.push([start, end]); return true; }; /** * Your MyCalendarTwo object will be instantiated and called as such: * var obj = new MyCalendarTwo() - * var param_1 = obj.book(start,end) + * var param_1 = obj.book(startTime,endTime) */ ``` @@ -286,7 +294,23 @@ MyCalendarTwo.prototype.book = function (start, end) { -### Solution 2 +### Solution 2: Segment Tree + +A segment tree divides the entire interval into multiple non-contiguous subintervals, with the number of subintervals not exceeding $\log(\textit{width})$. To update the value of an element, only $\log(\textit{width})$ intervals need to be updated, and these intervals are all contained within a larger interval that includes the element. When modifying intervals, a **lazy mark** is used to ensure efficiency. + +- Each node of the segment tree represents an interval; +- The segment tree has a unique root node representing the entire statistical range, such as $[1, N]$; +- Each leaf node of the segment tree represents a unit interval of length 1, $[x, x]$; +- For each internal node $[l, r]$, its left child is $[l, \textit{mid}]$ and its right child is $[\textit{mid} + 1, r]$, where $\textit{mid} = \lfloor(l + r) / 2\rfloor$ (i.e., floor division). + +For this problem, the segment tree nodes maintain the following information: + +1. The maximum number of bookings within the interval $v$ +2. Lazy mark $\textit{add}$ + +Since the time range is $10^9$, which is very large, we use dynamic node creation. + +The time complexity is $O(n \times \log n)$, and the space complexity is $O(n)$. Here, $n$ is the number of bookings. @@ -294,7 +318,7 @@ MyCalendarTwo.prototype.book = function (start, end) { ```python class Node: - def __init__(self, l, r): + def __init__(self, l: int, r: int): self.left = None self.right = None self.l = l @@ -306,9 +330,9 @@ class Node: class SegmentTree: def __init__(self): - self.root = Node(1, int(1e9 + 1)) + self.root = Node(1, 10**9 + 1) - def modify(self, l, r, v, node=None): + def modify(self, l: int, r: int, v: int, node: Node = None): if l > r: return if node is None: @@ -324,7 +348,7 @@ class SegmentTree: self.modify(l, r, v, node.right) self.pushup(node) - def query(self, l, r, node=None): + def query(self, l: int, r: int, node: Node = None) -> int: if l > r: return 0 if node is None: @@ -339,10 +363,10 @@ class SegmentTree: v = max(v, self.query(l, r, node.right)) return v - def pushup(self, node): + def pushup(self, node: Node): node.v = max(node.left.v, node.right.v) - def pushdown(self, node): + def pushdown(self, node: Node): if node.left is None: node.left = Node(node.l, node.mid) if node.right is None: @@ -359,16 +383,16 @@ class MyCalendarTwo: def __init__(self): self.tree = SegmentTree() - def book(self, start: int, end: int) -> bool: - if self.tree.query(start + 1, end) >= 2: + def book(self, startTime: int, endTime: int) -> bool: + if self.tree.query(startTime + 1, endTime) >= 2: return False - self.tree.modify(start + 1, end, 1) + self.tree.modify(startTime + 1, endTime, 1) return True # Your MyCalendarTwo object will be instantiated and called as such: # obj = MyCalendarTwo() -# param_1 = obj.book(start,end) +# param_1 = obj.book(startTime,endTime) ``` #### Java @@ -468,11 +492,11 @@ class MyCalendarTwo { public MyCalendarTwo() { } - public boolean book(int start, int end) { - if (tree.query(start + 1, end) >= 2) { + public boolean book(int startTime, int endTime) { + if (tree.query(startTime + 1, endTime) >= 2) { return false; } - tree.modify(start + 1, end, 1); + tree.modify(startTime + 1, endTime, 1); return true; } } @@ -480,7 +504,7 @@ class MyCalendarTwo { /** * Your MyCalendarTwo object will be instantiated and called as such: * MyCalendarTwo obj = new MyCalendarTwo(); - * boolean param_1 = obj.book(start,end); + * boolean param_1 = obj.book(startTime,endTime); */ ``` @@ -489,77 +513,90 @@ class MyCalendarTwo { ```cpp class Node { public: + int l, r, mid, v, add; Node* left; Node* right; - int l; - int r; - int mid; - int v; - int add; - Node(int l, int r) { - this->l = l; - this->r = r; - this->mid = (l + r) >> 1; - this->left = this->right = nullptr; - v = add = 0; - } + Node(int l, int r) + : l(l) + , r(r) + , mid((l + r) >> 1) + , v(0) + , add(0) + , left(nullptr) + , right(nullptr) {} }; class SegmentTree { -private: +public: Node* root; -public: SegmentTree() { root = new Node(1, 1e9 + 1); } - void modify(int l, int r, int v) { - modify(l, r, v, root); - } + void modify(int l, int r, int v, Node* node = nullptr) { + if (l > r) { + return; + } + if (node == nullptr) { + node = root; + } - void modify(int l, int r, int v, Node* node) { - if (l > r) return; if (node->l >= l && node->r <= r) { node->v += v; node->add += v; return; } pushdown(node); - if (l <= node->mid) modify(l, r, v, node->left); - if (r > node->mid) modify(l, r, v, node->right); + if (l <= node->mid) { + modify(l, r, v, node->left); + } + if (r > node->mid) { + modify(l, r, v, node->right); + } pushup(node); } - int query(int l, int r) { - return query(l, r, root); - } + int query(int l, int r, Node* node = nullptr) { + if (l > r) { + return 0; + } + if (node == nullptr) { + node = root; + } - int query(int l, int r, Node* node) { - if (l > r) return 0; - if (node->l >= l && node->r <= r) return node->v; + if (node->l >= l && node->r <= r) { + return node->v; + } pushdown(node); int v = 0; - if (l <= node->mid) v = max(v, query(l, r, node->left)); - if (r > node->mid) v = max(v, query(l, r, node->right)); + if (l <= node->mid) { + v = max(v, query(l, r, node->left)); + } + if (r > node->mid) { + v = max(v, query(l, r, node->right)); + } return v; } +private: void pushup(Node* node) { node->v = max(node->left->v, node->right->v); } void pushdown(Node* node) { - if (!node->left) node->left = new Node(node->l, node->mid); - if (!node->right) node->right = new Node(node->mid + 1, node->r); + if (node->left == nullptr) { + node->left = new Node(node->l, node->mid); + } + if (node->right == nullptr) { + node->right = new Node(node->mid + 1, node->r); + } if (node->add) { - Node* left = node->left; - Node* right = node->right; - left->v += node->add; - right->v += node->add; - left->add += node->add; - right->add += node->add; + node->left->v += node->add; + node->right->v += node->add; + node->left->add += node->add; + node->right->add += node->add; node->add = 0; } } @@ -567,14 +604,15 @@ public: class MyCalendarTwo { public: - SegmentTree* tree = new SegmentTree(); + SegmentTree tree; - MyCalendarTwo() { - } + MyCalendarTwo() {} - bool book(int start, int end) { - if (tree->query(start + 1, end) >= 2) return false; - tree->modify(start + 1, end, 1); + bool book(int startTime, int endTime) { + if (tree.query(startTime + 1, endTime) >= 2) { + return false; + } + tree.modify(startTime + 1, endTime, 1); return true; } }; @@ -582,7 +620,7 @@ public: /** * Your MyCalendarTwo object will be instantiated and called as such: * MyCalendarTwo* obj = new MyCalendarTwo(); - * bool param_1 = obj->book(start,end); + * bool param_1 = obj->book(startTime,endTime); */ ``` @@ -679,89 +717,230 @@ func Constructor() MyCalendarTwo { return MyCalendarTwo{newSegmentTree()} } -func (this *MyCalendarTwo) Book(start int, end int) bool { - if this.tree.query(start+1, end, this.tree.root) >= 2 { +func (this *MyCalendarTwo) Book(startTime int, endTime int) bool { + if this.tree.query(startTime+1, endTime, this.tree.root) >= 2 { return false } - this.tree.modify(start+1, end, 1, this.tree.root) + this.tree.modify(startTime+1, endTime, 1, this.tree.root) return true } /** * Your MyCalendarTwo object will be instantiated and called as such: * obj := Constructor(); - * param_1 := obj.Book(start,end); + * param_1 := obj.Book(startTime,endTime); */ ``` - - - +#### TypeScript - +```ts +class Node { + left: Node | null = null; + right: Node | null = null; + l: number; + r: number; + mid: number; + v: number = 0; + add: number = 0; + + constructor(l: number, r: number) { + this.l = l; + this.r = r; + this.mid = (l + r) >> 1; + } +} -### Solution 3: Line Sweep +class SegmentTree { + private root: Node = new Node(1, 1e9 + 1); - + modify(l: number, r: number, v: number, node: Node | null = this.root): void { + if (l > r || !node) { + return; + } + if (node.l >= l && node.r <= r) { + node.v += v; + node.add += v; + return; + } + this.pushdown(node); + if (l <= node.mid) { + this.modify(l, r, v, node.left); + } + if (r > node.mid) { + this.modify(l, r, v, node.right); + } + this.pushup(node); + } -#### TypeScript + query(l: number, r: number, node: Node | null = this.root): number { + if (l > r || !node) { + return 0; + } + if (node.l >= l && node.r <= r) { + return node.v; + } + this.pushdown(node); + let v = 0; + if (l <= node.mid) { + v = Math.max(v, this.query(l, r, node.left)); + } + if (r > node.mid) { + v = Math.max(v, this.query(l, r, node.right)); + } + return v; + } -```ts -class MyCalendarTwo { - #OVERLAPS = 2; - #cnt: Record = {}; + private pushup(node: Node): void { + if (node.left && node.right) { + node.v = Math.max(node.left.v, node.right.v); + } + } - book(start: number, end: number): boolean { - this.#cnt[start] = (this.#cnt[start] ?? 0) + 1; - this.#cnt[end] = (this.#cnt[end] ?? 0) - 1; + private pushdown(node: Node): void { + if (!node.left) { + node.left = new Node(node.l, node.mid); + } + if (!node.right) { + node.right = new Node(node.mid + 1, node.r); + } + if (node.add) { + let left = node.left; + let right = node.right; + left.add += node.add; + right.add += node.add; + left.v += node.add; + right.v += node.add; + node.add = 0; + } + } +} - let sum = 0; - for (const v of Object.values(this.#cnt)) { - sum += v; - if (sum > this.#OVERLAPS) { - this.#cnt[start]--; - this.#cnt[end]++; +class MyCalendarTwo { + private tree: SegmentTree = new SegmentTree(); - if (!this.#cnt[start]) delete this.#cnt[start]; - if (!this.#cnt[end]) delete this.#cnt[end]; + constructor() {} - return false; - } + book(startTime: number, endTime: number): boolean { + if (this.tree.query(startTime + 1, endTime) >= 2) { + return false; } - + this.tree.modify(startTime + 1, endTime, 1); return true; } } + +/** + * Your MyCalendarTwo object will be instantiated and called as such: + * var obj = new MyCalendarTwo() + * var param_1 = obj.book(startTime,endTime) + */ ``` #### JavaScript ```js -class MyCalendarTwo { - #OVERLAPS = 2; - #cnt = {}; +class Node { + constructor(l, r) { + this.left = null; + this.right = null; + this.l = l; + this.r = r; + this.mid = (l + r) >> 1; + this.v = 0; + this.add = 0; + } +} - book(start, end) { - this.#cnt[start] = (this.#cnt[start] ?? 0) + 1; - this.#cnt[end] = (this.#cnt[end] ?? 0) - 1; +class SegmentTree { + constructor() { + this.root = new Node(1, 1e9 + 1); + } - let sum = 0; - for (const v of Object.values(this.#cnt)) { - sum += v; - if (sum > this.#OVERLAPS) { - this.#cnt[start]--; - this.#cnt[end]++; + modify(l, r, v, node = this.root) { + if (l > r || !node) { + return; + } + if (node.l >= l && node.r <= r) { + node.v += v; + node.add += v; + return; + } + this.pushdown(node); + if (l <= node.mid) { + this.modify(l, r, v, node.left); + } + if (r > node.mid) { + this.modify(l, r, v, node.right); + } + this.pushup(node); + } - if (!this.#cnt[start]) delete this.#cnt[start]; - if (!this.#cnt[end]) delete this.#cnt[end]; + query(l, r, node = this.root) { + if (l > r || !node) { + return 0; + } + if (node.l >= l && node.r <= r) { + return node.v; + } + this.pushdown(node); + let v = 0; + if (l <= node.mid) { + v = Math.max(v, this.query(l, r, node.left)); + } + if (r > node.mid) { + v = Math.max(v, this.query(l, r, node.right)); + } + return v; + } - return false; - } + pushup(node) { + if (node.left && node.right) { + node.v = Math.max(node.left.v, node.right.v); } + } - return true; + pushdown(node) { + if (!node.left) { + node.left = new Node(node.l, node.mid); + } + if (!node.right) { + node.right = new Node(node.mid + 1, node.r); + } + if (node.add) { + const left = node.left; + const right = node.right; + left.add += node.add; + right.add += node.add; + left.v += node.add; + right.v += node.add; + node.add = 0; + } } } + +var MyCalendarTwo = function () { + this.tree = new SegmentTree(); +}; + +/** + * @param {number} startTime + * @param {number} endTime + * @return {boolean} + */ +MyCalendarTwo.prototype.book = function (startTime, endTime) { + if (this.tree.query(startTime + 1, endTime) >= 2) { + return false; + } + this.tree.modify(startTime + 1, endTime, 1); + return true; +}; + +/** + * Your MyCalendarTwo object will be instantiated and called as such: + * var obj = new MyCalendarTwo() + * var param_1 = obj.book(startTime,endTime) + */ ``` diff --git a/solution/0700-0799/0731.My Calendar II/Solution.cpp b/solution/0700-0799/0731.My Calendar II/Solution.cpp index f2b351b1a1932..241a66e582052 100644 --- a/solution/0700-0799/0731.My Calendar II/Solution.cpp +++ b/solution/0700-0799/0731.My Calendar II/Solution.cpp @@ -1,28 +1,29 @@ class MyCalendarTwo { public: - map m; - MyCalendarTwo() { } - bool book(int start, int end) { - ++m[start]; - --m[end]; + bool book(int startTime, int endTime) { + ++m[startTime]; + --m[endTime]; int s = 0; for (auto& [_, v] : m) { s += v; if (s > 2) { - --m[start]; - ++m[end]; + --m[startTime]; + ++m[endTime]; return false; } } return true; } + +private: + map m; }; /** * Your MyCalendarTwo object will be instantiated and called as such: * MyCalendarTwo* obj = new MyCalendarTwo(); - * bool param_1 = obj->book(start,end); - */ \ No newline at end of file + * bool param_1 = obj->book(startTime,endTime); + */ diff --git a/solution/0700-0799/0731.My Calendar II/Solution.go b/solution/0700-0799/0731.My Calendar II/Solution.go index 09209de3faa20..5a6452c3aae7f 100644 --- a/solution/0700-0799/0731.My Calendar II/Solution.go +++ b/solution/0700-0799/0731.My Calendar II/Solution.go @@ -1,28 +1,30 @@ type MyCalendarTwo struct { - *redblacktree.Tree + rbt *redblacktree.Tree[int, int] } func Constructor() MyCalendarTwo { - return MyCalendarTwo{redblacktree.NewWithIntComparator()} + return MyCalendarTwo{rbt: redblacktree.New[int, int]()} } -func (this *MyCalendarTwo) Book(start int, end int) bool { - add := func(key, val int) { - if v, ok := this.Get(key); ok { - this.Put(key, v.(int)+val) +func (this *MyCalendarTwo) Book(startTime int, endTime int) bool { + merge := func(x, v int) { + c, _ := this.rbt.Get(x) + if c+v == 0 { + this.rbt.Remove(x) } else { - this.Put(key, val) + this.rbt.Put(x, c+v) } } - add(start, 1) - add(end, -1) + + merge(startTime, 1) + merge(endTime, -1) + s := 0 - it := this.Iterator() - for it.Next() { - s += it.Value().(int) + for _, v := range this.rbt.Values() { + s += v if s > 2 { - add(start, -1) - add(end, 1) + merge(startTime, -1) + merge(endTime, 1) return false } } @@ -32,5 +34,5 @@ func (this *MyCalendarTwo) Book(start int, end int) bool { /** * Your MyCalendarTwo object will be instantiated and called as such: * obj := Constructor(); - * param_1 := obj.Book(start,end); - */ \ No newline at end of file + * param_1 := obj.Book(startTime,endTime); + */ diff --git a/solution/0700-0799/0731.My Calendar II/Solution.java b/solution/0700-0799/0731.My Calendar II/Solution.java index d12d9d782bd4b..58a429c566f25 100644 --- a/solution/0700-0799/0731.My Calendar II/Solution.java +++ b/solution/0700-0799/0731.My Calendar II/Solution.java @@ -1,18 +1,18 @@ class MyCalendarTwo { - private Map tm = new TreeMap<>(); + private final Map tm = new TreeMap<>(); public MyCalendarTwo() { } - public boolean book(int start, int end) { - tm.put(start, tm.getOrDefault(start, 0) + 1); - tm.put(end, tm.getOrDefault(end, 0) - 1); + public boolean book(int startTime, int endTime) { + tm.merge(startTime, 1, Integer::sum); + tm.merge(endTime, -1, Integer::sum); int s = 0; for (int v : tm.values()) { s += v; if (s > 2) { - tm.put(start, tm.get(start) - 1); - tm.put(end, tm.get(end) + 1); + tm.merge(startTime, -1, Integer::sum); + tm.merge(endTime, 1, Integer::sum); return false; } } @@ -23,5 +23,5 @@ public boolean book(int start, int end) { /** * Your MyCalendarTwo object will be instantiated and called as such: * MyCalendarTwo obj = new MyCalendarTwo(); - * boolean param_1 = obj.book(start,end); - */ \ No newline at end of file + * boolean param_1 = obj.book(startTime,endTime); + */ diff --git a/solution/0700-0799/0731.My Calendar II/Solution.js b/solution/0700-0799/0731.My Calendar II/Solution.js index 53cfad370e6f7..a8bdb406bb189 100644 --- a/solution/0700-0799/0731.My Calendar II/Solution.js +++ b/solution/0700-0799/0731.My Calendar II/Solution.js @@ -1,32 +1,34 @@ var MyCalendarTwo = function () { - this.events = []; - this.overlaps = []; + this.tm = {}; }; /** - * @param {number} start - * @param {number} end + * @param {number} startTime + * @param {number} endTime * @return {boolean} */ -MyCalendarTwo.prototype.book = function (start, end) { - for (let [s, e] of this.overlaps) { - if (Math.max(start, s) < Math.min(end, e)) { - return false; - } - } +MyCalendarTwo.prototype.book = function (startTime, endTime) { + this.tm[startTime] = (this.tm[startTime] || 0) + 1; + this.tm[endTime] = (this.tm[endTime] || 0) - 1; + let s = 0; - for (let [s, e] of this.events) { - if (Math.max(start, s) < Math.min(end, e)) { - this.overlaps.push([Math.max(start, s), Math.min(end, e)]); + for (const v of Object.values(this.tm)) { + s += v; + if (s > 2) { + if (--this.tm[startTime] === 0) { + delete this.tm[startTime]; + } + if (++this.tm[endTime] === 0) { + delete this.tm[endTime]; + } + return false; } } - - this.events.push([start, end]); return true; }; /** * Your MyCalendarTwo object will be instantiated and called as such: * var obj = new MyCalendarTwo() - * var param_1 = obj.book(start,end) + * var param_1 = obj.book(startTime,endTime) */ diff --git a/solution/0700-0799/0731.My Calendar II/Solution.py b/solution/0700-0799/0731.My Calendar II/Solution.py index 1944c9e7d7c79..5d7b8be7283db 100644 --- a/solution/0700-0799/0731.My Calendar II/Solution.py +++ b/solution/0700-0799/0731.My Calendar II/Solution.py @@ -5,19 +5,19 @@ class MyCalendarTwo: def __init__(self): self.sd = SortedDict() - def book(self, start: int, end: int) -> bool: - self.sd[start] = self.sd.get(start, 0) + 1 - self.sd[end] = self.sd.get(end, 0) - 1 + def book(self, startTime: int, endTime: int) -> bool: + self.sd[startTime] = self.sd.get(startTime, 0) + 1 + self.sd[endTime] = self.sd.get(endTime, 0) - 1 s = 0 for v in self.sd.values(): s += v if s > 2: - self.sd[start] -= 1 - self.sd[end] += 1 + self.sd[startTime] -= 1 + self.sd[endTime] += 1 return False return True # Your MyCalendarTwo object will be instantiated and called as such: # obj = MyCalendarTwo() -# param_1 = obj.book(start,end) +# param_1 = obj.book(startTime,endTime) diff --git a/solution/0700-0799/0731.My Calendar II/Solution.ts b/solution/0700-0799/0731.My Calendar II/Solution.ts index 28dba25de72ab..8226594bc2681 100644 --- a/solution/0700-0799/0731.My Calendar II/Solution.ts +++ b/solution/0700-0799/0731.My Calendar II/Solution.ts @@ -1,26 +1,24 @@ class MyCalendarTwo { - private events: [number, number][]; - private overlaps: [number, number][]; + private tm: Record = {}; - constructor() { - this.events = []; - this.overlaps = []; - } + constructor() {} - book(start: number, end: number): boolean { - for (const [s, e] of this.overlaps) { - if (Math.max(start, s) < Math.min(end, e)) { + book(startTime: number, endTime: number): boolean { + this.tm[startTime] = (this.tm[startTime] ?? 0) + 1; + this.tm[endTime] = (this.tm[endTime] ?? 0) - 1; + let s = 0; + for (const v of Object.values(this.tm)) { + s += v; + if (s > 2) { + if (--this.tm[startTime] === 0) { + delete this.tm[startTime]; + } + if (++this.tm[endTime] === 0) { + delete this.tm[endTime]; + } return false; } } - - for (const [s, e] of this.events) { - if (Math.max(start, s) < Math.min(end, e)) { - this.overlaps.push([Math.max(start, s), Math.min(end, e)]); - } - } - - this.events.push([start, end]); return true; } } @@ -28,5 +26,5 @@ class MyCalendarTwo { /** * Your MyCalendarTwo object will be instantiated and called as such: * var obj = new MyCalendarTwo() - * var param_1 = obj.book(start,end) + * var param_1 = obj.book(startTime,endTime) */ diff --git a/solution/0700-0799/0731.My Calendar II/Solution2.cpp b/solution/0700-0799/0731.My Calendar II/Solution2.cpp index 47110e6e8afc7..3a88a46587f3d 100644 --- a/solution/0700-0799/0731.My Calendar II/Solution2.cpp +++ b/solution/0700-0799/0731.My Calendar II/Solution2.cpp @@ -1,76 +1,89 @@ class Node { public: + int l, r, mid, v, add; Node* left; Node* right; - int l; - int r; - int mid; - int v; - int add; - Node(int l, int r) { - this->l = l; - this->r = r; - this->mid = (l + r) >> 1; - this->left = this->right = nullptr; - v = add = 0; - } + Node(int l, int r) + : l(l) + , r(r) + , mid((l + r) >> 1) + , v(0) + , add(0) + , left(nullptr) + , right(nullptr) {} }; class SegmentTree { -private: +public: Node* root; -public: SegmentTree() { root = new Node(1, 1e9 + 1); } - void modify(int l, int r, int v) { - modify(l, r, v, root); - } + void modify(int l, int r, int v, Node* node = nullptr) { + if (l > r) { + return; + } + if (node == nullptr) { + node = root; + } - void modify(int l, int r, int v, Node* node) { - if (l > r) return; if (node->l >= l && node->r <= r) { node->v += v; node->add += v; return; } pushdown(node); - if (l <= node->mid) modify(l, r, v, node->left); - if (r > node->mid) modify(l, r, v, node->right); + if (l <= node->mid) { + modify(l, r, v, node->left); + } + if (r > node->mid) { + modify(l, r, v, node->right); + } pushup(node); } - int query(int l, int r) { - return query(l, r, root); - } + int query(int l, int r, Node* node = nullptr) { + if (l > r) { + return 0; + } + if (node == nullptr) { + node = root; + } - int query(int l, int r, Node* node) { - if (l > r) return 0; - if (node->l >= l && node->r <= r) return node->v; + if (node->l >= l && node->r <= r) { + return node->v; + } pushdown(node); int v = 0; - if (l <= node->mid) v = max(v, query(l, r, node->left)); - if (r > node->mid) v = max(v, query(l, r, node->right)); + if (l <= node->mid) { + v = max(v, query(l, r, node->left)); + } + if (r > node->mid) { + v = max(v, query(l, r, node->right)); + } return v; } +private: void pushup(Node* node) { node->v = max(node->left->v, node->right->v); } void pushdown(Node* node) { - if (!node->left) node->left = new Node(node->l, node->mid); - if (!node->right) node->right = new Node(node->mid + 1, node->r); + if (node->left == nullptr) { + node->left = new Node(node->l, node->mid); + } + if (node->right == nullptr) { + node->right = new Node(node->mid + 1, node->r); + } if (node->add) { - Node* left = node->left; - Node* right = node->right; - left->v += node->add; - right->v += node->add; - left->add += node->add; - right->add += node->add; + node->left->v += node->add; + node->right->v += node->add; + node->left->add += node->add; + node->right->add += node->add; node->add = 0; } } @@ -78,14 +91,15 @@ class SegmentTree { class MyCalendarTwo { public: - SegmentTree* tree = new SegmentTree(); + SegmentTree tree; - MyCalendarTwo() { - } + MyCalendarTwo() {} - bool book(int start, int end) { - if (tree->query(start + 1, end) >= 2) return false; - tree->modify(start + 1, end, 1); + bool book(int startTime, int endTime) { + if (tree.query(startTime + 1, endTime) >= 2) { + return false; + } + tree.modify(startTime + 1, endTime, 1); return true; } }; @@ -93,5 +107,5 @@ class MyCalendarTwo { /** * Your MyCalendarTwo object will be instantiated and called as such: * MyCalendarTwo* obj = new MyCalendarTwo(); - * bool param_1 = obj->book(start,end); - */ \ No newline at end of file + * bool param_1 = obj->book(startTime,endTime); + */ diff --git a/solution/0700-0799/0731.My Calendar II/Solution2.go b/solution/0700-0799/0731.My Calendar II/Solution2.go index 32cab0ad93397..5f107056e94fb 100644 --- a/solution/0700-0799/0731.My Calendar II/Solution2.go +++ b/solution/0700-0799/0731.My Calendar II/Solution2.go @@ -88,16 +88,16 @@ func Constructor() MyCalendarTwo { return MyCalendarTwo{newSegmentTree()} } -func (this *MyCalendarTwo) Book(start int, end int) bool { - if this.tree.query(start+1, end, this.tree.root) >= 2 { +func (this *MyCalendarTwo) Book(startTime int, endTime int) bool { + if this.tree.query(startTime+1, endTime, this.tree.root) >= 2 { return false } - this.tree.modify(start+1, end, 1, this.tree.root) + this.tree.modify(startTime+1, endTime, 1, this.tree.root) return true } /** * Your MyCalendarTwo object will be instantiated and called as such: * obj := Constructor(); - * param_1 := obj.Book(start,end); - */ \ No newline at end of file + * param_1 := obj.Book(startTime,endTime); + */ diff --git a/solution/0700-0799/0731.My Calendar II/Solution2.java b/solution/0700-0799/0731.My Calendar II/Solution2.java index f77e48f08772d..ced94ed7ea4ca 100644 --- a/solution/0700-0799/0731.My Calendar II/Solution2.java +++ b/solution/0700-0799/0731.My Calendar II/Solution2.java @@ -92,11 +92,11 @@ class MyCalendarTwo { public MyCalendarTwo() { } - public boolean book(int start, int end) { - if (tree.query(start + 1, end) >= 2) { + public boolean book(int startTime, int endTime) { + if (tree.query(startTime + 1, endTime) >= 2) { return false; } - tree.modify(start + 1, end, 1); + tree.modify(startTime + 1, endTime, 1); return true; } } @@ -104,5 +104,5 @@ public boolean book(int start, int end) { /** * Your MyCalendarTwo object will be instantiated and called as such: * MyCalendarTwo obj = new MyCalendarTwo(); - * boolean param_1 = obj.book(start,end); - */ \ No newline at end of file + * boolean param_1 = obj.book(startTime,endTime); + */ diff --git a/solution/0700-0799/0731.My Calendar II/Solution2.js b/solution/0700-0799/0731.My Calendar II/Solution2.js new file mode 100644 index 0000000000000..1225d06015715 --- /dev/null +++ b/solution/0700-0799/0731.My Calendar II/Solution2.js @@ -0,0 +1,101 @@ +class Node { + constructor(l, r) { + this.left = null; + this.right = null; + this.l = l; + this.r = r; + this.mid = (l + r) >> 1; + this.v = 0; + this.add = 0; + } +} + +class SegmentTree { + constructor() { + this.root = new Node(1, 1e9 + 1); + } + + modify(l, r, v, node = this.root) { + if (l > r || !node) { + return; + } + if (node.l >= l && node.r <= r) { + node.v += v; + node.add += v; + return; + } + this.pushdown(node); + if (l <= node.mid) { + this.modify(l, r, v, node.left); + } + if (r > node.mid) { + this.modify(l, r, v, node.right); + } + this.pushup(node); + } + + query(l, r, node = this.root) { + if (l > r || !node) { + return 0; + } + if (node.l >= l && node.r <= r) { + return node.v; + } + this.pushdown(node); + let v = 0; + if (l <= node.mid) { + v = Math.max(v, this.query(l, r, node.left)); + } + if (r > node.mid) { + v = Math.max(v, this.query(l, r, node.right)); + } + return v; + } + + pushup(node) { + if (node.left && node.right) { + node.v = Math.max(node.left.v, node.right.v); + } + } + + pushdown(node) { + if (!node.left) { + node.left = new Node(node.l, node.mid); + } + if (!node.right) { + node.right = new Node(node.mid + 1, node.r); + } + if (node.add) { + const left = node.left; + const right = node.right; + left.add += node.add; + right.add += node.add; + left.v += node.add; + right.v += node.add; + node.add = 0; + } + } +} + +var MyCalendarTwo = function () { + this.tree = new SegmentTree(); +}; + +/** + * @param {number} startTime + * @param {number} endTime + * @return {boolean} + */ +MyCalendarTwo.prototype.book = function (startTime, endTime) { + if (this.tree.query(startTime + 1, endTime) >= 2) { + return false; + } + this.tree.modify(startTime + 1, endTime, 1); + return true; +}; + +/** + * Your MyCalendarTwo object will be instantiated and called as such: + * var obj = new MyCalendarTwo() + * var param_1 = obj.book(startTime,endTime) + */ diff --git a/solution/0700-0799/0731.My Calendar II/Solution2.py b/solution/0700-0799/0731.My Calendar II/Solution2.py index dd5797afe62fd..11c700d70e8b2 100644 --- a/solution/0700-0799/0731.My Calendar II/Solution2.py +++ b/solution/0700-0799/0731.My Calendar II/Solution2.py @@ -1,5 +1,5 @@ class Node: - def __init__(self, l, r): + def __init__(self, l: int, r: int): self.left = None self.right = None self.l = l @@ -11,9 +11,9 @@ def __init__(self, l, r): class SegmentTree: def __init__(self): - self.root = Node(1, int(1e9 + 1)) + self.root = Node(1, 10**9 + 1) - def modify(self, l, r, v, node=None): + def modify(self, l: int, r: int, v: int, node: Node = None): if l > r: return if node is None: @@ -29,7 +29,7 @@ def modify(self, l, r, v, node=None): self.modify(l, r, v, node.right) self.pushup(node) - def query(self, l, r, node=None): + def query(self, l: int, r: int, node: Node = None) -> int: if l > r: return 0 if node is None: @@ -44,10 +44,10 @@ def query(self, l, r, node=None): v = max(v, self.query(l, r, node.right)) return v - def pushup(self, node): + def pushup(self, node: Node): node.v = max(node.left.v, node.right.v) - def pushdown(self, node): + def pushdown(self, node: Node): if node.left is None: node.left = Node(node.l, node.mid) if node.right is None: @@ -64,13 +64,13 @@ class MyCalendarTwo: def __init__(self): self.tree = SegmentTree() - def book(self, start: int, end: int) -> bool: - if self.tree.query(start + 1, end) >= 2: + def book(self, startTime: int, endTime: int) -> bool: + if self.tree.query(startTime + 1, endTime) >= 2: return False - self.tree.modify(start + 1, end, 1) + self.tree.modify(startTime + 1, endTime, 1) return True # Your MyCalendarTwo object will be instantiated and called as such: # obj = MyCalendarTwo() -# param_1 = obj.book(start,end) +# param_1 = obj.book(startTime,endTime) diff --git a/solution/0700-0799/0731.My Calendar II/Solution2.ts b/solution/0700-0799/0731.My Calendar II/Solution2.ts new file mode 100644 index 0000000000000..2fb79e2c380d0 --- /dev/null +++ b/solution/0700-0799/0731.My Calendar II/Solution2.ts @@ -0,0 +1,100 @@ +class Node { + left: Node | null = null; + right: Node | null = null; + l: number; + r: number; + mid: number; + v: number = 0; + add: number = 0; + + constructor(l: number, r: number) { + this.l = l; + this.r = r; + this.mid = (l + r) >> 1; + } +} + +class SegmentTree { + private root: Node = new Node(1, 1e9 + 1); + + modify(l: number, r: number, v: number, node: Node | null = this.root): void { + if (l > r || !node) { + return; + } + if (node.l >= l && node.r <= r) { + node.v += v; + node.add += v; + return; + } + this.pushdown(node); + if (l <= node.mid) { + this.modify(l, r, v, node.left); + } + if (r > node.mid) { + this.modify(l, r, v, node.right); + } + this.pushup(node); + } + + query(l: number, r: number, node: Node | null = this.root): number { + if (l > r || !node) { + return 0; + } + if (node.l >= l && node.r <= r) { + return node.v; + } + this.pushdown(node); + let v = 0; + if (l <= node.mid) { + v = Math.max(v, this.query(l, r, node.left)); + } + if (r > node.mid) { + v = Math.max(v, this.query(l, r, node.right)); + } + return v; + } + + private pushup(node: Node): void { + if (node.left && node.right) { + node.v = Math.max(node.left.v, node.right.v); + } + } + + private pushdown(node: Node): void { + if (!node.left) { + node.left = new Node(node.l, node.mid); + } + if (!node.right) { + node.right = new Node(node.mid + 1, node.r); + } + if (node.add) { + let left = node.left; + let right = node.right; + left.add += node.add; + right.add += node.add; + left.v += node.add; + right.v += node.add; + node.add = 0; + } + } +} + +class MyCalendarTwo { + private tree: SegmentTree = new SegmentTree(); + + constructor() {} + + book(startTime: number, endTime: number): boolean { + if (this.tree.query(startTime + 1, endTime) >= 2) { + return false; + } + this.tree.modify(startTime + 1, endTime, 1); + return true; + } +} + +/** + * Your MyCalendarTwo object will be instantiated and called as such: + * var obj = new MyCalendarTwo() + * var param_1 = obj.book(startTime,endTime) + */ diff --git a/solution/0700-0799/0731.My Calendar II/Solution3.js b/solution/0700-0799/0731.My Calendar II/Solution3.js deleted file mode 100644 index 86fcb55a25ca9..0000000000000 --- a/solution/0700-0799/0731.My Calendar II/Solution3.js +++ /dev/null @@ -1,25 +0,0 @@ -class MyCalendarTwo { - #OVERLAPS = 2; - #cnt = {}; - - book(start, end) { - this.#cnt[start] = (this.#cnt[start] ?? 0) + 1; - this.#cnt[end] = (this.#cnt[end] ?? 0) - 1; - - let sum = 0; - for (const v of Object.values(this.#cnt)) { - sum += v; - if (sum > this.#OVERLAPS) { - this.#cnt[start]--; - this.#cnt[end]++; - - if (!this.#cnt[start]) delete this.#cnt[start]; - if (!this.#cnt[end]) delete this.#cnt[end]; - - return false; - } - } - - return true; - } -} diff --git a/solution/0700-0799/0731.My Calendar II/Solution3.ts b/solution/0700-0799/0731.My Calendar II/Solution3.ts deleted file mode 100644 index 29d9836756197..0000000000000 --- a/solution/0700-0799/0731.My Calendar II/Solution3.ts +++ /dev/null @@ -1,25 +0,0 @@ -class MyCalendarTwo { - #OVERLAPS = 2; - #cnt: Record = {}; - - book(start: number, end: number): boolean { - this.#cnt[start] = (this.#cnt[start] ?? 0) + 1; - this.#cnt[end] = (this.#cnt[end] ?? 0) - 1; - - let sum = 0; - for (const v of Object.values(this.#cnt)) { - sum += v; - if (sum > this.#OVERLAPS) { - this.#cnt[start]--; - this.#cnt[end]++; - - if (!this.#cnt[start]) delete this.#cnt[start]; - if (!this.#cnt[end]) delete this.#cnt[end]; - - return false; - } - } - - return true; - } -}