Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add solutions to lc problem: No.0715 #1954

Merged
merged 1 commit into from
Nov 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 151 additions & 2 deletions solution/0700-0799/0715.Range Module/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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$ 为数据范围。

<!-- tabs:start -->

### **Python3**
Expand All @@ -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
Expand All @@ -77,6 +85,8 @@ class Node:


class SegmentTree:
__slots__ = ['root']

def __init__(self):
self.root = Node()

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
*/
```

### **...**

```
Expand Down
160 changes: 158 additions & 2 deletions solution/0700-0799/0715.Range Module/README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,29 @@ 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.

<!-- tabs:start -->

### **Python3**

```python
class Node:
__slots__ = ['left', 'right', 'add', 'v']

def __init__(self):
self.left = None
self.right = None
Expand All @@ -62,6 +77,8 @@ class Node:


class SegmentTree:
__slots__ = ['root']

def __init__(self):
self.root = Node()

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
*/
```

### **...**

```
Expand Down
Loading