|
9 | 9 |
|
10 | 10 | **要求**:返回 `k` 次操作后的数组。
|
11 | 11 |
|
12 |
| -**说明**: |
13 |
| - |
14 |
| -- |
15 |
| - |
16 | 12 | **示例**:
|
17 | 13 |
|
18 | 14 | ```
|
|
34 | 30 |
|
35 | 31 | 这样构建线段树的时间复杂度为 $O(log_2n)$,单次区间更新的时间复杂度为 $O(log_2n)$,单次区间查询的时间复杂度为 $O(log_2n)$。总体时间复杂度为 $O(log_2n)$。
|
36 | 32 |
|
37 |
| -## 代码 |
38 |
| - |
39 |
| -### 思路 1 代码: |
| 33 | +### 思路 1:线段树代码 |
40 | 34 |
|
41 | 35 | ```Python
|
42 |
| -class TreeNode: |
| 36 | +# 线段树的节点类 |
| 37 | +class SegTreeNode: |
43 | 38 | 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 | + |
50 | 45 | # 线段树类
|
51 | 46 | class SegmentTree:
|
| 47 | + # 初始化线段树接口 |
52 | 48 | def __init__(self, nums, function):
|
53 | 49 | 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 是一个函数,左右区间的聚合方法 |
57 | 53 | if self.size > 0:
|
58 | 54 | 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] |
61 | 79 | def __build(self, index, left, right):
|
62 | 80 | self.tree[index].left = left
|
63 | 81 | self.tree[index].right = right
|
64 |
| - if left == right: # 叶子节点,节点值为对应位置的元素值 |
| 82 | + if left == right: # 叶子节点,节点值为对应位置的元素值 |
65 | 83 | self.tree[index].val = self.nums[left]
|
66 | 84 | 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 |
90 | 100 | 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) # 当前节点所在区间大小 |
136 | 123 | self.tree[index].val += val * interval_size # 当前节点所在区间每个元素值增加 val
|
137 | 124 | 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] 无关 |
139 | 149 | 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) |
140 | 170 |
|
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 的节点所在区间的左右子节点的值和懒惰标记 |
154 | 172 | def __pushdown(self, index):
|
155 | 173 | 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 |
160 | 184 | 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 |
164 | 191 | 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 # 更新当前节点的懒惰标记 |
174 | 195 |
|
175 | 196 | class Solution:
|
176 | 197 | def getModifiedArray(self, length: int, updates: List[List[int]]) -> List[int]:
|
|
0 commit comments