@@ -40,7 +40,9 @@ queryRange(16, 17): true (尽管执行了删除操作,区间 [16, 17) 中的
40
40
41
41
- 暂无
42
42
43
- ## 思路
43
+ ## 二分法
44
+
45
+ ### 思路
44
46
45
47
直观的思路是使用端点记录已经被跟踪的区间,我们需要记录的区间信息大概是这样的:[ (1,2),(3,6),(8,12)] ,这表示 [ 1,2), [ 3,6), [ 8,12) 被跟踪。
46
48
@@ -105,12 +107,12 @@ class RangeModule(object):
105
107
106
108
![ 区间更新逻辑] ( https://p.ipic.vip/ovosah.jpg )
107
109
108
- ## 关键点解析
110
+ ### 关键点解析
109
111
110
112
- 二分查找的灵活使用(最左插入和最右插入)
111
113
- 将区间一维化处理
112
114
113
- ## 代码
115
+ ### 代码
114
116
115
117
为了明白 Python 代码的含义,你需要明白 bisect_left 和 bisect_right,关于这两点我在[ 二分查找] ( https://github.com/azl397985856/leetcode/blob/master/91/binary-search.md " 二分查找 ") 专题讲地很清楚了,大家可以看一下。实际上这两者的区别只在于目标数组有目标值的情况,因此如果你搞不懂,可以尝试代入这种特殊情况理解。
116
118
@@ -155,9 +157,117 @@ addRange 和 removeRange 中使用 bisect_left 找到左端点 l,使用 bisect
155
157
156
158
** 复杂度分析**
157
159
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
+ ** 复杂度分析**
160
267
268
+ - 时间复杂度:$O(logn)$,其中 n 为跟踪的数据规模
269
+ - 空间复杂度:$O(logn)$,其中 n 为跟踪的数据规模
270
+ -
161
271
更多题解可以访问我的 LeetCode 题解仓库:https://github.com/azl397985856/leetcode 。 目前已经 37K star 啦。
162
272
163
273
关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。
0 commit comments