Skip to content

Commit ade2d16

Browse files
author
杨世超
committed
Update 0370. 区间加法.md
1 parent 1fee32b commit ade2d16

File tree

1 file changed

+139
-118
lines changed

1 file changed

+139
-118
lines changed

Solutions/0370. 区间加法.md

Lines changed: 139 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,6 @@
99

1010
**要求**:返回 `k` 次操作后的数组。
1111

12-
**说明**
13-
14-
-
15-
1612
**示例**
1713

1814
```
@@ -34,143 +30,168 @@
3430

3531
这样构建线段树的时间复杂度为 $O(log_2n)$,单次区间更新的时间复杂度为 $O(log_2n)$,单次区间查询的时间复杂度为 $O(log_2n)$。总体时间复杂度为 $O(log_2n)$。
3632

37-
## 代码
38-
39-
### 思路 1 代码:
33+
### 思路 1:线段树代码
4034

4135
```Python
42-
class TreeNode:
36+
# 线段树的节点类
37+
class SegTreeNode:
4338
def __init__(self, val=0):
44-
self.left = -1 # 区间左边界
45-
self.right = -1 # 区间右边界
46-
self.val = val # 节点值(区间值)
47-
self.lazy_tag = 0 # 区间和问题的延迟更新标记
48-
49-
39+
self.left = -1 # 区间左边界
40+
self.right = -1 # 区间右边界
41+
self.val = val # 节点值(区间值)
42+
self.lazy_tag = None # 区间和问题的延迟更新标记
43+
44+
5045
# 线段树类
5146
class SegmentTree:
47+
# 初始化线段树接口
5248
def __init__(self, nums, function):
5349
self.size = len(nums)
54-
self.tree = [TreeNode() for _ in range(4 * self.size)] # 维护 TreeNode 数组
55-
self.nums = nums # 原始数据
56-
self.function = function # function 是一个函数,左右区间的聚合方法
50+
self.tree = [SegTreeNode() for _ in range(4 * self.size)] # 维护 SegTreeNode 数组
51+
self.nums = nums # 原始数据
52+
self.function = function # function 是一个函数,左右区间的聚合方法
5753
if self.size > 0:
5854
self.__build(0, 0, self.size - 1)
59-
60-
# 构建线段树,节点的存储下标为 index,节点的区间为 [left, right]
55+
56+
# 单点更新接口:将 nums[i] 更改为 val
57+
def update_point(self, i, val):
58+
self.nums[i] = val
59+
self.__update_point(i, val, 0)
60+
61+
# 区间更新接口:将区间为 [q_left, q_right] 上的所有元素值加上 val
62+
def update_interval(self, q_left, q_right, val):
63+
self.__update_interval(q_left, q_right, val, 0)
64+
65+
# 区间查询接口:查询区间为 [q_left, q_right] 的区间值
66+
def query_interval(self, q_left, q_right):
67+
return self.__query_interval(q_left, q_right, 0)
68+
69+
# 获取 nums 数组接口:返回 nums 数组
70+
def get_nums(self):
71+
for i in range(self.size):
72+
self.nums[i] = self.query_interval(i, i)
73+
return self.nums
74+
75+
76+
# 以下为内部实现方法
77+
78+
# 构建线段树实现方法:节点的存储下标为 index,节点的区间为 [left, right]
6179
def __build(self, index, left, right):
6280
self.tree[index].left = left
6381
self.tree[index].right = right
64-
if left == right: # 叶子节点,节点值为对应位置的元素值
82+
if left == right: # 叶子节点,节点值为对应位置的元素值
6583
self.tree[index].val = self.nums[left]
6684
return
67-
68-
mid = left + (right - left) // 2 # 左右节点划分点
69-
left_index = index * 2 + 1 # 左子节点的存储下标
70-
right_index = index * 2 + 2 # 右子节点的存储下标
71-
self.__build(left_index, left, mid) # 递归创建左子树
72-
self.__build(right_index, mid + 1, right) # 递归创建右子树
73-
self.__pushup(index) # 向上更新节点的区间值
74-
75-
# 向上更新下标为 index 的节点区间值,节点的区间值等于该节点左右子节点元素值的聚合计算结果
76-
def __pushup(self, index):
77-
left_index = index * 2 + 1 # 左子节点的存储下标
78-
right_index = index * 2 + 2 # 右子节点的存储下标
79-
self.tree[index].val = self.function(self.tree[left_index].val, self.tree[right_index].val)
80-
81-
# 单点更新,将 nums[i] 更改为 val
82-
def update_point(self, i, val):
83-
self.nums[i] = val
84-
self.__update_point(i, val, 0, 0, self.size - 1)
85-
86-
# 单点更新,将 nums[i] 更改为 val。节点的存储下标为 index,节点的区间为 [left, right]
87-
def __update_point(self, i, val, index, left, right):
88-
if self.tree[index].left == self.tree[index].right:
89-
self.tree[index].val = val # 叶子节点,节点值修改为 val
85+
86+
mid = left + (right - left) // 2 # 左右节点划分点
87+
left_index = index * 2 + 1 # 左子节点的存储下标
88+
right_index = index * 2 + 2 # 右子节点的存储下标
89+
self.__build(left_index, left, mid) # 递归创建左子树
90+
self.__build(right_index, mid + 1, right) # 递归创建右子树
91+
self.__pushup(index) # 向上更新节点的区间值
92+
93+
# 单点更新实现方法:将 nums[i] 更改为 val,节点的存储下标为 index
94+
def __update_point(self, i, val, index):
95+
left = self.tree[index].left
96+
right = self.tree[index].right
97+
98+
if left == right:
99+
self.tree[index].val = val # 叶子节点,节点值修改为 val
90100
return
91-
92-
mid = left + (right - left) // 2 # 左右节点划分点
93-
left_index = index * 2 + 1 # 左子节点的存储下标
94-
right_index = index * 2 + 2 # 右子节点的存储下标
95-
if i <= mid: # 在左子树中更新节点值
96-
self.__update_point(i, val, left_index, left, mid)
97-
else: # 在右子树中更新节点值
98-
self.__update_point(i, val, right_index, mid + 1, right)
99-
self.__pushup(index) # 向上更新节点的区间值
100-
101-
# 区间查询,查询区间为 [q_left, q_right] 的区间值
102-
def query_interval(self, q_left, q_right):
103-
return self.__query_interval(q_left, q_right, 0, 0, self.size - 1)
104-
105-
# 区间查询,在线段树的 [left, right] 区间范围中搜索区间为 [q_left, q_right] 的区间值
106-
def __query_interval(self, q_left, q_right, index, left, right):
107-
if left >= q_left and right <= q_right: # 节点所在区间被 [q_left, q_right] 所覆盖
108-
return self.tree[index].val # 直接返回节点值
109-
if right < q_left or left > q_right: # 节点所在区间与 [q_left, q_right] 无关
110-
return 0
111-
112-
if self.tree[index].lazy_tag: # 需要向下更新节点所在区间的左右子节点的值和懒惰标记
113-
self.__pushdown(index)
114-
115-
mid = left + (right - left) // 2 # 左右节点划分点
116-
left_index = index * 2 + 1 # 左子节点的存储下标
117-
right_index = index * 2 + 2 # 右子节点的存储下标
118-
res_left = 0 # 左子树查询结果
119-
res_right = 0 # 右子树查询结果
120-
if q_left <= mid: # 在左子树中查询
121-
res_left = self.__query_interval(q_left, q_right, left_index, left, mid)
122-
if q_right > mid: # 在右子树中查询
123-
res_right = self.__query_interval(q_left, q_right, right_index, mid + 1, right)
124-
return self.function(res_left, res_right) # 返回左右子树元素值的聚合计算结果
125-
126-
# 区间更新,将区间为 [q_left, q_right] 上的元素值修改为 val
127-
def update_interval(self, q_left, q_right, val):
128-
self.__update_interval(q_left, q_right, val, 0, 0, self.size - 1)
129-
130-
# 区间更新
131-
def __update_interval(self, q_left, q_right, val, index, left, right):
132-
133-
if left >= q_left and right <= q_right: # 节点所在区间被 [q_left, q_right] 所覆盖
134-
self.tree[index].lazy_tag += val # 将当前节点的延迟标记增加 val
135-
interval_size = (right - left + 1) # 当前节点所在区间大小
101+
102+
mid = left + (right - left) // 2 # 左右节点划分点
103+
left_index = index * 2 + 1 # 左子节点的存储下标
104+
right_index = index * 2 + 2 # 右子节点的存储下标
105+
if i <= mid: # 在左子树中更新节点值
106+
self.__update_point(i, val, left_index)
107+
else: # 在右子树中更新节点值
108+
self.__update_point(i, val, right_index)
109+
110+
self.__pushup(index) # 向上更新节点的区间值
111+
112+
# 区间更新实现方法
113+
def __update_interval(self, q_left, q_right, val, index):
114+
left = self.tree[index].left
115+
right = self.tree[index].right
116+
117+
if left >= q_left and right <= q_right: # 节点所在区间被 [q_left, q_right] 所覆盖
118+
if self.tree[index].lazy_tag is not None:
119+
self.tree[index].lazy_tag += val # 将当前节点的延迟标记增加 val
120+
else:
121+
self.tree[index].lazy_tag = val # 将当前节点的延迟标记增加 val
122+
interval_size = (right - left + 1) # 当前节点所在区间大小
136123
self.tree[index].val += val * interval_size # 当前节点所在区间每个元素值增加 val
137124
return
138-
if right < q_left or left > q_right: # 节点所在区间与 [q_left, q_right] 无关
125+
126+
if right < q_left or left > q_right: # 节点所在区间与 [q_left, q_right] 无关
127+
return
128+
129+
self.__pushdown(index) # 向下更新节点的区间值
130+
131+
mid = left + (right - left) // 2 # 左右节点划分点
132+
left_index = index * 2 + 1 # 左子节点的存储下标
133+
right_index = index * 2 + 2 # 右子节点的存储下标
134+
if q_left <= mid: # 在左子树中更新区间值
135+
self.__update_interval(q_left, q_right, val, left_index)
136+
if q_right > mid: # 在右子树中更新区间值
137+
self.__update_interval(q_left, q_right, val, right_index)
138+
139+
self.__pushup(index) # 向上更新节点的区间值
140+
141+
# 区间查询实现方法:在线段树中搜索区间为 [q_left, q_right] 的区间值
142+
def __query_interval(self, q_left, q_right, index):
143+
left = self.tree[index].left
144+
right = self.tree[index].right
145+
146+
if left >= q_left and right <= q_right: # 节点所在区间被 [q_left, q_right] 所覆盖
147+
return self.tree[index].val # 直接返回节点值
148+
if right < q_left or left > q_right: # 节点所在区间与 [q_left, q_right] 无关
139149
return 0
150+
151+
self.__pushdown(index)
152+
153+
mid = left + (right - left) // 2 # 左右节点划分点
154+
left_index = index * 2 + 1 # 左子节点的存储下标
155+
right_index = index * 2 + 2 # 右子节点的存储下标
156+
res_left = 0 # 左子树查询结果
157+
res_right = 0 # 右子树查询结果
158+
if q_left <= mid: # 在左子树中查询
159+
res_left = self.__query_interval(q_left, q_right, left_index)
160+
if q_right > mid: # 在右子树中查询
161+
res_right = self.__query_interval(q_left, q_right, right_index)
162+
163+
return self.function(res_left, res_right) # 返回左右子树元素值的聚合计算结果
164+
165+
# 向上更新实现方法:更新下标为 index 的节点区间值 等于 该节点左右子节点元素值的聚合计算结果
166+
def __pushup(self, index):
167+
left_index = index * 2 + 1 # 左子节点的存储下标
168+
right_index = index * 2 + 2 # 右子节点的存储下标
169+
self.tree[index].val = self.function(self.tree[left_index].val, self.tree[right_index].val)
140170

141-
if self.tree[index].lazy_tag: # 需要向下更新节点所在区间的左右子节点的值和懒惰标记
142-
self.__pushdown(index)
143-
144-
mid = left + (right - left) // 2 # 左右节点划分点
145-
left_index = index * 2 + 1 # 左子节点的存储下标
146-
right_index = index * 2 + 2 # 右子节点的存储下标
147-
if q_left <= mid: # 在左子树中更新区间值
148-
self.__update_interval(q_left, q_right, val, left_index, left, mid)
149-
if q_right > mid: # 在右子树中更新区间值
150-
self.__update_interval(q_left, q_right, val, right_index, mid + 1, right)
151-
self.__pushup(index)
152-
153-
# 向下更新下标为 index 的节点所在区间的左右子节点的值和懒惰标记
171+
# 向下更新实现方法:更新下标为 index 的节点所在区间的左右子节点的值和懒惰标记
154172
def __pushdown(self, index):
155173
lazy_tag = self.tree[index].lazy_tag
156-
left_index = index * 2 + 1 # 左子节点的存储下标
157-
right_index = index * 2 + 2 # 右子节点的存储下标
158-
159-
self.tree[left_index].lazy_tag += lazy_tag # 更新左子节点懒惰标记
174+
if lazy_tag is None:
175+
return
176+
177+
left_index = index * 2 + 1 # 左子节点的存储下标
178+
right_index = index * 2 + 2 # 右子节点的存储下标
179+
180+
if self.tree[left_index].lazy_tag is not None:
181+
self.tree[left_index].lazy_tag += lazy_tag # 更新左子节点懒惰标记
182+
else:
183+
self.tree[left_index].lazy_tag = lazy_tag
160184
left_size = (self.tree[left_index].right - self.tree[left_index].left + 1)
161-
self.tree[left_index].val += lazy_tag * left_size # 左子节点每个元素值增加 lazy_tag
162-
163-
self.tree[right_index].lazy_tag += lazy_tag # 更新右子节点懒惰标记
185+
self.tree[left_index].val += lazy_tag * left_size # 左子节点每个元素值增加 lazy_tag
186+
187+
if self.tree[right_index].lazy_tag is not None:
188+
self.tree[right_index].lazy_tag += lazy_tag # 更新右子节点懒惰标记
189+
else:
190+
self.tree[right_index].lazy_tag = lazy_tag
164191
right_size = (self.tree[right_index].right - self.tree[right_index].left + 1)
165-
self.tree[right_index].val += lazy_tag * right_size # 右子节点每个元素值增加 lazy_tag
166-
167-
self.tree[index].lazy_tag = 0 # 更新当前节点的懒惰标记
168-
169-
def get_nums(self):
170-
for i in range(self.size):
171-
self.nums[i] = self.query_interval(i, i)
172-
return self.nums
173-
192+
self.tree[right_index].val += lazy_tag * right_size # 右子节点每个元素值增加 lazy_tag
193+
194+
self.tree[index].lazy_tag = None # 更新当前节点的懒惰标记
174195

175196
class Solution:
176197
def getModifiedArray(self, length: int, updates: List[List[int]]) -> List[int]:

0 commit comments

Comments
 (0)