Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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

Lines changed: 115 additions & 5 deletions
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)

AltStyle によって変換されたページ (->オリジナル) /