Skip to content

Commit 6e477e5

Browse files
author
robot
committed
2 parents b5ce692 + 3fe689b commit 6e477e5

File tree

1 file changed

+115
-5
lines changed

1 file changed

+115
-5
lines changed

problems/715.range-module.md

+115-5
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ queryRange(16, 17): true (尽管执行了删除操作,区间 [16, 17) 中的
4040

4141
- 暂无
4242

43-
## 思路
43+
## 二分法
44+
45+
### 思路
4446

4547
直观的思路是使用端点记录已经被跟踪的区间,我们需要记录的区间信息大概是这样的:[(1,2),(3,6),(8,12)],这表示 [1,2), [3,6), [8,12) 被跟踪。
4648

@@ -105,12 +107,12 @@ class RangeModule(object):
105107

106108
![区间更新逻辑](https://p.ipic.vip/ovosah.jpg)
107109

108-
## 关键点解析
110+
### 关键点解析
109111

110112
- 二分查找的灵活使用(最左插入和最右插入)
111113
- 将区间一维化处理
112114

113-
## 代码
115+
### 代码
114116

115117
为了明白 Python 代码的含义,你需要明白 bisect_left 和 bisect_right,关于这两点我在[二分查找](https://github.com/azl397985856/leetcode/blob/master/91/binary-search.md "二分查找")专题讲地很清楚了,大家可以看一下。实际上这两者的区别只在于目标数组有目标值的情况,因此如果你搞不懂,可以尝试代入这种特殊情况理解。
116118

@@ -155,9 +157,117 @@ addRange 和 removeRange 中使用 bisect_left 找到左端点 l,使用 bisect
155157

156158
**复杂度分析**
157159

158-
- 时间复杂度:$O(m * n)$,其中 m 和 n 分别为 A 和 B 的 长度。
159-
- 空间复杂度:$O(m * n)$,其中 m 和 n 分别为 A 和 B 的 长度。
160+
- 时间复杂度:$O(logn)$,其中 n 为跟踪的数据规模
161+
- 空间复杂度:$O(logn)$,其中 n 为跟踪的数据规模
162+
163+
## 动态开点线段树
164+
165+
### 思路
166+
167+
我们可以用线段树来解决区间更新问题。
168+
169+
由于数据规模很大, 因此动态开点就比较适合了。
170+
171+
插入的话就是区间 update 为 1, 删除就是区间 update 为 0,查找的话就看下区间和是否是区间长度即可。
172+
173+
代码为我的插件(公众号力扣加加回复插件可以获得)中提供的模板代码,稍微改了一下 query。这是因为普通的 query 是查找区间和, 而我们如果不修改, 那么会超时。我们的区间和可以提前退出。如果区间和不等于区间长度就提前退出即可。
174+
175+
### 代码
176+
177+
代码支持:Python3
178+
179+
Python3 Code:
180+
181+
```py
182+
183+
class Node:
184+
def __init__(self, l, r):
185+
self.left = None # 左孩子的指针
186+
self.right = None # 右孩子的指针
187+
self.l = l # 区间左端点
188+
self.r = r # 区间右端点
189+
self.m = (l + r) >> 1 # 中点
190+
self.v = 0 # 当前值
191+
self.add = -1 # 懒标记
192+
193+
class SegmentTree:
194+
def __init__(self,n):
195+
# 默认就一个根节点,不 build 出整个树,节省空间
196+
self.root = Node(0,n-1) # 根节点
197+
198+
def update(self, l, r, v, node):
199+
if l > node.r or r < node.l:
200+
return
201+
if l <= node.l and node.r <= r:
202+
node.v = (node.r - node.l + 1) * v
203+
node.add = v # 做了一个标记
204+
return
205+
self.__pushdown(node) # 动态开点。为子节点赋值,这个值就从 add 传递过来
206+
if l <= node.m:
207+
self.update(l, r, v, node.left)
208+
if r > node.m:
209+
self.update(l, r, v, node.right)
210+
self.__pushup(node) # 动态开点结束后,修复当前节点的值
211+
212+
def query(self, l, r,node):
213+
if l > node.r or r < node.l:
214+
return False
215+
if l <= node.l and node.r <= r:
216+
return node.v == node.r - node.l + 1
217+
self.__pushdown(node) # 动态开点。为子节点赋值,这个值就从 add 传递过来
218+
ans = True
219+
if l <= node.m:
220+
ans = self.query(l, r, node.left)
221+
if ans and r > node.m:
222+
ans = self.query(l, r, node.right)
223+
return ans
224+
225+
def __pushdown(self,node):
226+
if node.left is None:
227+
node.left = Node(node.l, node.m)
228+
if node.right is None:
229+
node.right = Node(node.m + 1, node.r)
230+
if node.add != -1:
231+
node.left.v = (node.left.r - node.left.l + 1) * node.add
232+
node.right.v = (node.right.r - node.right.l + 1) * node.add
233+
node.left.add = node.add
234+
node.right.add = node.add
235+
node.add = -1
236+
237+
def __pushup(self,node):
238+
node.v = node.left.v + node.right.v
239+
240+
def updateSum(self,index,val):
241+
self.update(index,index,val,self.root)
242+
243+
def querySum(self,left,right):
244+
return self.query(left,right,self.root)
245+
246+
class RangeModule:
247+
def __init__(self):
248+
self.tree = SegmentTree(10 ** 9)
249+
250+
def addRange(self, left: int, right: int) -> None:
251+
self.tree.update(left, right - 1, 1, self.tree.root)
252+
253+
def queryRange(self, left: int, right: int) -> bool:
254+
return not not self.tree.querySum(left, right - 1)
255+
256+
def removeRange(self, left: int, right: int) -> None:
257+
self.tree.update(left, right - 1, 0, self.tree.root)
258+
259+
# Your RangeModule object will be instantiated and called as such:
260+
# obj = RangeModule()
261+
# obj.addRange(left,right)
262+
# param_2 = obj.queryRange(left,right)
263+
# obj.removeRange(left,right)
264+
```
265+
266+
**复杂度分析**
160267

268+
- 时间复杂度:$O(logn)$,其中 n 为跟踪的数据规模
269+
- 空间复杂度:$O(logn)$,其中 n 为跟踪的数据规模
270+
-
161271
更多题解可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 37K star 啦。
162272

163273
关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。

0 commit comments

Comments
 (0)