diff --git a/solution/0700-0799/0715.Range Module/README.md b/solution/0700-0799/0715.Range Module/README.md index bd9f39d5bfc91..288955e3b34c9 100644 --- a/solution/0700-0799/0715.Range Module/README.md +++ b/solution/0700-0799/0715.Range Module/README.md @@ -54,13 +54,19 @@ rangeModule.queryRange(16, 17); 返回 true (尽管执行了删除操作,区 **方法一:线段树** -线段树将整个区间分割为多个不连续的子区间,子区间的数量不超过 $log(width)$。更新某个元素的值,只需要更新 $log(width)$ 个区间,并且这些区间都包含在一个包含该元素的大区间内。区间修改时,需要使用**懒标记**保证效率。 +根据题目描述,我们需要维护一个区间集合,支持区间的添加、删除和查询操作。对于区间的添加和删除操作,我们可以使用线段树来维护区间集合。 + +线段树将整个区间分割为多个不连续的子区间,子区间的数量不超过 $\log(width)$。更新某个元素的值,只需要更新 $\log(width)$ 个区间,并且这些区间都包含在一个包含该元素的大区间内。区间修改时,需要使用**懒标记**保证效率。 - 线段树的每个节点代表一个区间; - 线段树具有唯一的根节点,代表的区间是整个统计范围,如 $[1,N]$; - 线段树的每个叶子节点代表一个长度为 $1$ 的元区间 $[x,x]$; - 对于每个内部节点 $[l,r]$,它的左儿子是 $[l,mid]$,右儿子是 $[mid+1,r]$, 其中 $mid=⌊(l+r)/2⌋$ (即向下取整)。 +由于题目数据范围较大,我们可以使用动态开点的线段树来实现。动态开点的线段树是指,我们只在需要的时候才开点,而不是一开始就开好所有的点。这样可以节省空间,但是需要使用**懒标记**来维护区间修改。 + +时间复杂度方面,每次操作的时间复杂度为 $O(\log n)$。空间复杂度为 $O(m \times \log n)$。其中 $m$ 为操作次数,而 $n$ 为数据范围。 + ### **Python3** @@ -69,6 +75,8 @@ rangeModule.queryRange(16, 17); 返回 true (尽管执行了删除操作,区 ```python class Node: + __slots__ = ['left', 'right', 'add', 'v'] + def __init__(self): self.left = None self.right = None @@ -77,6 +85,8 @@ class Node: class SegmentTree: + __slots__ = ['root'] + def __init__(self): self.root = Node() @@ -141,7 +151,6 @@ class RangeModule: def removeRange(self, left: int, right: int) -> None: self.tree.modify(left, right - 1, -1) - # Your RangeModule object will be instantiated and called as such: # obj = RangeModule() # obj.addRange(left,right) @@ -492,6 +501,146 @@ func (this *RangeModule) RemoveRange(left int, right int) { */ ``` +### **TypeScript** + +```ts +class Node { + left: Node | null; + right: Node | null; + add: number; + v: boolean; + + constructor() { + this.left = null; + this.right = null; + this.add = 0; + this.v = false; + } +} + +class SegmentTree { + private root: Node; + + constructor() { + this.root = new Node(); + } + + modify( + left: number, + right: number, + v: number, + l: number = 1, + r: number = 1e9, + node: Node | null = null, + ): void { + if (node === null) { + node = this.root; + } + + if (l >= left && r <= right) { + node.v = v === 1; + node.add = v; + return; + } + + this.pushdown(node); + + const mid = (l + r) >> 1; + + if (left <= mid) { + this.modify(left, right, v, l, mid, node.left); + } + + if (right > mid) { + this.modify(left, right, v, mid + 1, r, node.right); + } + + this.pushup(node); + } + + query( + left: number, + right: number, + l: number = 1, + r: number = 1e9, + node: Node | null = null, + ): boolean { + if (node === null) { + node = this.root; + } + + if (l >= left && r <= right) { + return node.v; + } + + this.pushdown(node); + + const mid = (l + r) >> 1; + let result = true; + + if (left <= mid) { + result = result && this.query(left, right, l, mid, node.left); + } + + if (right > mid) { + result = result && this.query(left, right, mid + 1, r, node.right); + } + + return result; + } + + pushup(node: Node): void { + node.v = !!(node.left && node.left.v && node.right && node.right.v); + } + + pushdown(node: Node): void { + if (node.left === null) { + node.left = new Node(); + } + + if (node.right === null) { + node.right = new Node(); + } + + if (node.add !== 0) { + node.left.add = node.add; + node.right.add = node.add; + node.left.v = node.add === 1; + node.right.v = node.add === 1; + node.add = 0; + } + } +} + +class RangeModule { + private tree: SegmentTree; + + constructor() { + this.tree = new SegmentTree(); + } + + addRange(left: number, right: number): void { + this.tree.modify(left, right - 1, 1); + } + + queryRange(left: number, right: number): boolean { + return this.tree.query(left, right - 1); + } + + removeRange(left: number, right: number): void { + this.tree.modify(left, right - 1, -1); + } +} + +/** + * Your RangeModule object will be instantiated and called as such: + * var obj = new RangeModule() + * obj.addRange(left,right) + * var param_2 = obj.queryRange(left,right) + * obj.removeRange(left,right) + */ +``` + ### **...** ``` diff --git a/solution/0700-0799/0715.Range Module/README_EN.md b/solution/0700-0799/0715.Range Module/README_EN.md index 1e7fd2ca8da7b..578a4b8494cab 100644 --- a/solution/0700-0799/0715.Range Module/README_EN.md +++ b/solution/0700-0799/0715.Range Module/README_EN.md @@ -46,7 +46,20 @@ rangeModule.queryRange(16, 17); // return True, (The number 16 in [16, 17) is st ## Solutions -Segment Tree. +**Solution 1: Segment Tree** + +According to the problem description, we need to maintain a set of intervals, supporting operations of interval addition, deletion, and query. For the addition and deletion of intervals, we can use a segment tree to maintain the set of intervals. + +The segment tree divides the entire interval into multiple non-continuous sub-intervals, the number of which does not exceed $\log(width)$. To update the value of an element, only $\log(width)$ intervals need to be updated, and these intervals are all included in a large interval containing the element. When modifying the interval, we need to 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 statistical range, such as $[1,N]$; +- Each leaf node of the segment tree represents an elementary interval of length $1$, $[x,x]$; +- For each internal node $[l,r]$, its left child is $[l,mid]$, and the right child is $[mid+1,r]$, where $mid=⌊(l+r)/2⌋$ (rounded down). + +Due to the large data range of the problem, we can implement it with a dynamically allocated segment tree. A dynamically allocated segment tree means that we only allocate nodes when needed, instead of allocating all nodes at the beginning. This can save space, but it requires **lazy propagation** to maintain interval modification. + +In terms of time complexity, the time complexity of each operation is $O(\log n)$. The space complexity is $O(m \times \log n)$. Here, $m$ is the number of operations, and $n$ is the data range. @@ -54,6 +67,8 @@ Segment Tree. ```python class Node: + __slots__ = ['left', 'right', 'add', 'v'] + def __init__(self): self.left = None self.right = None @@ -62,6 +77,8 @@ class Node: class SegmentTree: + __slots__ = ['root'] + def __init__(self): self.root = Node() @@ -126,7 +143,6 @@ class RangeModule: def removeRange(self, left: int, right: int) -> None: self.tree.modify(left, right - 1, -1) - # Your RangeModule object will be instantiated and called as such: # obj = RangeModule() # obj.addRange(left,right) @@ -475,6 +491,146 @@ func (this *RangeModule) RemoveRange(left int, right int) { */ ``` +### **TypeScript** + +```ts +class Node { + left: Node | null; + right: Node | null; + add: number; + v: boolean; + + constructor() { + this.left = null; + this.right = null; + this.add = 0; + this.v = false; + } +} + +class SegmentTree { + private root: Node; + + constructor() { + this.root = new Node(); + } + + modify( + left: number, + right: number, + v: number, + l: number = 1, + r: number = 1e9, + node: Node | null = null, + ): void { + if (node === null) { + node = this.root; + } + + if (l >= left && r <= right) { + node.v = v === 1; + node.add = v; + return; + } + + this.pushdown(node); + + const mid = (l + r) >> 1; + + if (left <= mid) { + this.modify(left, right, v, l, mid, node.left); + } + + if (right > mid) { + this.modify(left, right, v, mid + 1, r, node.right); + } + + this.pushup(node); + } + + query( + left: number, + right: number, + l: number = 1, + r: number = 1e9, + node: Node | null = null, + ): boolean { + if (node === null) { + node = this.root; + } + + if (l >= left && r <= right) { + return node.v; + } + + this.pushdown(node); + + const mid = (l + r) >> 1; + let result = true; + + if (left <= mid) { + result = result && this.query(left, right, l, mid, node.left); + } + + if (right > mid) { + result = result && this.query(left, right, mid + 1, r, node.right); + } + + return result; + } + + pushup(node: Node): void { + node.v = !!(node.left && node.left.v && node.right && node.right.v); + } + + pushdown(node: Node): void { + if (node.left === null) { + node.left = new Node(); + } + + if (node.right === null) { + node.right = new Node(); + } + + if (node.add !== 0) { + node.left.add = node.add; + node.right.add = node.add; + node.left.v = node.add === 1; + node.right.v = node.add === 1; + node.add = 0; + } + } +} + +class RangeModule { + private tree: SegmentTree; + + constructor() { + this.tree = new SegmentTree(); + } + + addRange(left: number, right: number): void { + this.tree.modify(left, right - 1, 1); + } + + queryRange(left: number, right: number): boolean { + return this.tree.query(left, right - 1); + } + + removeRange(left: number, right: number): void { + this.tree.modify(left, right - 1, -1); + } +} + +/** + * Your RangeModule object will be instantiated and called as such: + * var obj = new RangeModule() + * obj.addRange(left,right) + * var param_2 = obj.queryRange(left,right) + * obj.removeRange(left,right) + */ +``` + ### **...** ``` diff --git a/solution/0700-0799/0715.Range Module/Solution.py b/solution/0700-0799/0715.Range Module/Solution.py index d74da26f1cfbb..69b27d0c6d4e6 100644 --- a/solution/0700-0799/0715.Range Module/Solution.py +++ b/solution/0700-0799/0715.Range Module/Solution.py @@ -1,4 +1,6 @@ class Node: + __slots__ = ['left', 'right', 'add', 'v'] + def __init__(self): self.left = None self.right = None @@ -7,6 +9,8 @@ def __init__(self): class SegmentTree: + __slots__ = ['root'] + def __init__(self): self.root = Node() diff --git a/solution/0700-0799/0715.Range Module/Solution.ts b/solution/0700-0799/0715.Range Module/Solution.ts new file mode 100644 index 0000000000000..935cbc68a37de --- /dev/null +++ b/solution/0700-0799/0715.Range Module/Solution.ts @@ -0,0 +1,135 @@ +class Node { + left: Node | null; + right: Node | null; + add: number; + v: boolean; + + constructor() { + this.left = null; + this.right = null; + this.add = 0; + this.v = false; + } +} + +class SegmentTree { + private root: Node; + + constructor() { + this.root = new Node(); + } + + modify( + left: number, + right: number, + v: number, + l: number = 1, + r: number = 1e9, + node: Node | null = null, + ): void { + if (node === null) { + node = this.root; + } + + if (l >= left && r <= right) { + node.v = v === 1; + node.add = v; + return; + } + + this.pushdown(node); + + const mid = (l + r) >> 1; + + if (left <= mid) { + this.modify(left, right, v, l, mid, node.left); + } + + if (right > mid) { + this.modify(left, right, v, mid + 1, r, node.right); + } + + this.pushup(node); + } + + query( + left: number, + right: number, + l: number = 1, + r: number = 1e9, + node: Node | null = null, + ): boolean { + if (node === null) { + node = this.root; + } + + if (l >= left && r <= right) { + return node.v; + } + + this.pushdown(node); + + const mid = (l + r) >> 1; + let result = true; + + if (left <= mid) { + result = result && this.query(left, right, l, mid, node.left); + } + + if (right > mid) { + result = result && this.query(left, right, mid + 1, r, node.right); + } + + return result; + } + + pushup(node: Node): void { + node.v = !!(node.left && node.left.v && node.right && node.right.v); + } + + pushdown(node: Node): void { + if (node.left === null) { + node.left = new Node(); + } + + if (node.right === null) { + node.right = new Node(); + } + + if (node.add !== 0) { + node.left.add = node.add; + node.right.add = node.add; + node.left.v = node.add === 1; + node.right.v = node.add === 1; + node.add = 0; + } + } +} + +class RangeModule { + private tree: SegmentTree; + + constructor() { + this.tree = new SegmentTree(); + } + + addRange(left: number, right: number): void { + this.tree.modify(left, right - 1, 1); + } + + queryRange(left: number, right: number): boolean { + return this.tree.query(left, right - 1); + } + + removeRange(left: number, right: number): void { + this.tree.modify(left, right - 1, -1); + } +} + +/** + * Your RangeModule object will be instantiated and called as such: + * var obj = new RangeModule() + * obj.addRange(left,right) + * var param_2 = obj.queryRange(left,right) + * obj.removeRange(left,right) + */