From 7070b1ff0d3dfb1768e0e9c4be1fd548b10a2dc0 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Sat, 4 Jan 2025 09:42:40 +0800 Subject: [PATCH] feat: add solutions to lc problem: No.0732 (#3921) No.0732.My Calendar III --- .../0700-0799/0732.My Calendar III/README.md | 152 ++++++++++++++-- .../0732.My Calendar III/README_EN.md | 162 ++++++++++++++++-- .../0732.My Calendar III/Solution.cpp | 34 +++- .../0732.My Calendar III/Solution.py | 8 +- .../0732.My Calendar III/Solution.ts | 99 +++++++++++ 5 files changed, 413 insertions(+), 42 deletions(-) create mode 100644 solution/0700-0799/0732.My Calendar III/Solution.ts diff --git a/solution/0700-0799/0732.My Calendar III/README.md b/solution/0700-0799/0732.My Calendar III/README.md index 9eb077164cefe..fb8f2a0044e55 100644 --- a/solution/0700-0799/0732.My Calendar III/README.md +++ b/solution/0700-0799/0732.My Calendar III/README.md @@ -69,21 +69,21 @@ myCalendarThree.book(25, 55); // 返回 3 ### 方法一:线段树 -线段树将整个区间分割为多个不连续的子区间,子区间的数量不超过 $log(width)$。更新某个元素的值,只需要更新 $log(width)$ 个区间,并且这些区间都包含在一个包含该元素的大区间内。区间修改时,需要使用**懒标记**保证效率。 +线段树将整个区间分割为多个不连续的子区间,子区间的数量不超过 $log(\text{width})$。更新某个元素的值,只需要更新 $log(\text{width})$ 个区间,并且这些区间都包含在一个包含该元素的大区间内。区间修改时,需要使用**懒标记**保证效率。 - 线段树的每个节点代表一个区间; - 线段树具有唯一的根节点,代表的区间是整个统计范围,如 $[1,N]$; - 线段树的每个叶子节点代表一个长度为 $1$ 的元区间 $[x, x]$; -- 对于每个内部节点 $[l,r]$,它的左儿子是 $[l,mid]$,右儿子是 $[mid+1,r]$, 其中 $mid = ⌊(l+r)/2⌋$ (即向下取整)。 +- 对于每个内部节点 $[l,r]$,它的左儿子是 $[l,\text{mid}]$,右儿子是 $[\text{mid}+1,r]$, 其中 $\text{mid} = ⌊(l+r)/2⌋$ (即向下取整)。 对于本题,线段树节点维护的信息有: 1. 区间范围内被预定的次数的最大值 $v$ -1. 懒标记 $add$ +1. 懒标记 $\text{add}$ 由于时间范围为 $10^9$,非常大,因此我们采用动态开点。 -时间复杂度 $O(nlogn)$,其中 $n$ 表示日程安排的数量。 +时间复杂度 $O(n \times \log n)$,空间复杂度 $O(n)$,其中 $n$ 表示日程安排的数量。 @@ -105,7 +105,7 @@ class SegmentTree: def __init__(self): self.root = Node(1, int(1e9 + 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: @@ -121,7 +121,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: @@ -136,10 +136,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: @@ -312,15 +312,21 @@ public: } void modify(int l, int r, int v, Node* node) { - if (l > r) return; + 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); } @@ -329,12 +335,18 @@ public: } int query(int l, int r, Node* node) { - if (l > r) return 0; + if (l > r) { + return 0; + } 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; } @@ -343,8 +355,12 @@ public: } 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) { + node->left = new Node(node->l, node->mid); + } + if (!node->right) { + node->right = new Node(node->mid + 1, node->r); + } if (node->add) { Node* left = node->left; Node* right = node->right; @@ -483,6 +499,110 @@ func (this *MyCalendarThree) Book(start int, end int) int { */ ``` +#### 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; + } +} + +class SegmentTree { + private root: Node = new Node(1, 1e9 + 1); + + constructor() {} + + modify(l: number, r: number, v: number, node: Node = this.root): void { + if (l > r) { + 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 = this.root): number { + if (l > r) { + 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 { + node.v = Math.max(node.left!.v, node.right!.v); + } + + private pushdown(node: Node): void { + if (node.left === null) { + node.left = new Node(node.l, node.mid); + } + if (node.right === null) { + node.right = new Node(node.mid + 1, node.r); + } + if (node.add !== 0) { + 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; + } + } +} + +class MyCalendarThree { + private tree: SegmentTree; + + constructor() { + this.tree = new SegmentTree(); + } + + book(start: number, end: number): number { + this.tree.modify(start + 1, end, 1); + return this.tree.query(1, 1e9 + 1); + } +} + +/** + * Your MyCalendarThree object will be instantiated and called as such: + * var obj = new MyCalendarThree() + * var param_1 = obj.book(startTime, endTime) + */ +``` + diff --git a/solution/0700-0799/0732.My Calendar III/README_EN.md b/solution/0700-0799/0732.My Calendar III/README_EN.md index 9c095d8a0a49e..e9f19bdde0c0b 100644 --- a/solution/0700-0799/0732.My Calendar III/README_EN.md +++ b/solution/0700-0799/0732.My Calendar III/README_EN.md @@ -66,7 +66,23 @@ myCalendarThree.book(25, 55); // return 3 -### Solution 1 +### Solution 1: Segment Tree + +A segment tree divides the entire interval into multiple non-contiguous subintervals, with the number of subintervals not exceeding $\log(\text{width})$. To update the value of an element, we only need to update $\log(\text{width})$ intervals, and these intervals are all contained within a larger interval that includes the element. When modifying intervals, we use **lazy propagation** to ensure efficiency. + +- Each node of the segment tree represents an interval. +- The segment tree has a unique root node representing the entire 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, \text{mid}]$ and its right child is $[\text{mid} + 1, r]$, where $\text{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 times the interval has been booked, $v$. +2. Lazy propagation marker, $\text{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)$, where $n$ is the number of bookings. @@ -88,7 +104,7 @@ class SegmentTree: def __init__(self): self.root = Node(1, int(1e9 + 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: @@ -104,7 +120,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: @@ -119,10 +135,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: @@ -295,15 +311,21 @@ public: } void modify(int l, int r, int v, Node* node) { - if (l > r) return; + 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); } @@ -312,12 +334,18 @@ public: } int query(int l, int r, Node* node) { - if (l > r) return 0; + if (l > r) { + return 0; + } 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; } @@ -326,8 +354,12 @@ public: } 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) { + node->left = new Node(node->l, node->mid); + } + if (!node->right) { + node->right = new Node(node->mid + 1, node->r); + } if (node->add) { Node* left = node->left; Node* right = node->right; @@ -466,6 +498,110 @@ func (this *MyCalendarThree) Book(start int, end int) int { */ ``` +#### 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; + } +} + +class SegmentTree { + private root: Node = new Node(1, 1e9 + 1); + + constructor() {} + + modify(l: number, r: number, v: number, node: Node = this.root): void { + if (l > r) { + 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 = this.root): number { + if (l > r) { + 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 { + node.v = Math.max(node.left!.v, node.right!.v); + } + + private pushdown(node: Node): void { + if (node.left === null) { + node.left = new Node(node.l, node.mid); + } + if (node.right === null) { + node.right = new Node(node.mid + 1, node.r); + } + if (node.add !== 0) { + 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; + } + } +} + +class MyCalendarThree { + private tree: SegmentTree; + + constructor() { + this.tree = new SegmentTree(); + } + + book(start: number, end: number): number { + this.tree.modify(start + 1, end, 1); + return this.tree.query(1, 1e9 + 1); + } +} + +/** + * Your MyCalendarThree object will be instantiated and called as such: + * var obj = new MyCalendarThree() + * var param_1 = obj.book(startTime, endTime) + */ +``` + diff --git a/solution/0700-0799/0732.My Calendar III/Solution.cpp b/solution/0700-0799/0732.My Calendar III/Solution.cpp index 9a6b54976d6d0..51e87afc4ea27 100644 --- a/solution/0700-0799/0732.My Calendar III/Solution.cpp +++ b/solution/0700-0799/0732.My Calendar III/Solution.cpp @@ -31,15 +31,21 @@ class SegmentTree { } void modify(int l, int r, int v, Node* node) { - if (l > r) return; + 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); } @@ -48,12 +54,18 @@ class SegmentTree { } int query(int l, int r, Node* node) { - if (l > r) return 0; + if (l > r) { + return 0; + } 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; } @@ -62,8 +74,12 @@ class SegmentTree { } 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) { + node->left = new Node(node->l, node->mid); + } + if (!node->right) { + node->right = new Node(node->mid + 1, node->r); + } if (node->add) { Node* left = node->left; Node* right = node->right; @@ -94,4 +110,4 @@ class MyCalendarThree { * Your MyCalendarThree object will be instantiated and called as such: * MyCalendarThree* obj = new MyCalendarThree(); * int param_1 = obj->book(start,end); - */ \ No newline at end of file + */ diff --git a/solution/0700-0799/0732.My Calendar III/Solution.py b/solution/0700-0799/0732.My Calendar III/Solution.py index ba577eb3924cd..4dda8fc2a8339 100644 --- a/solution/0700-0799/0732.My Calendar III/Solution.py +++ b/solution/0700-0799/0732.My Calendar III/Solution.py @@ -13,7 +13,7 @@ class SegmentTree: def __init__(self): self.root = Node(1, int(1e9 + 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: diff --git a/solution/0700-0799/0732.My Calendar III/Solution.ts b/solution/0700-0799/0732.My Calendar III/Solution.ts new file mode 100644 index 0000000000000..770ecfd3df2f5 --- /dev/null +++ b/solution/0700-0799/0732.My Calendar III/Solution.ts @@ -0,0 +1,99 @@ +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); + + constructor() {} + + modify(l: number, r: number, v: number, node: Node = this.root): void { + if (l > r) { + 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 = this.root): number { + if (l > r) { + 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 { + node.v = Math.max(node.left!.v, node.right!.v); + } + + private pushdown(node: Node): void { + if (node.left === null) { + node.left = new Node(node.l, node.mid); + } + if (node.right === null) { + node.right = new Node(node.mid + 1, node.r); + } + if (node.add !== 0) { + 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; + } + } +} + +class MyCalendarThree { + private tree: SegmentTree; + + constructor() { + this.tree = new SegmentTree(); + } + + book(start: number, end: number): number { + this.tree.modify(start + 1, end, 1); + return this.tree.query(1, 1e9 + 1); + } +} + +/** + * Your MyCalendarThree object will be instantiated and called as such: + * var obj = new MyCalendarThree() + * var param_1 = obj.book(startTime, endTime) + */