@@ -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