|
33 | 33 |
|
34 | 34 | ## 解题思路
|
35 | 35 |
|
36 |
| -### 思路 1: |
| 36 | +### 思路 1:线段树 |
37 | 37 |
|
| 38 | +- 初始化一个长度为 `n`,值全为 `0` 的 `nums` 数组。 |
| 39 | +- 然后根据 `nums` 数组构建一棵线段树。每个线段树的节点类存储当前区间的左右边界和该区间的和。并且线段树使用延迟标记。 |
| 40 | +- 然后遍历三元组操作,进行区间累加运算。 |
| 41 | +- 最后从线段树中查询数组所有元素,返回该数组即可。 |
38 | 42 |
|
| 43 | +这样构建线段树的时间复杂度为 $O(log_2n)$,单次区间更新的时间复杂度为 $O(log_2n)$,单次区间查询的时间复杂度为 $O(log_2n)$。总体时间复杂度为 $O(log_2n)$。 |
39 | 44 |
|
40 |
| -## 代码 |
41 |
| - |
42 |
| -### 思路 1 代码: |
| 45 | +### 思路 1 线段树代码: |
43 | 46 |
|
44 | 47 | ```Python
|
45 |
| - |
| 48 | +# 线段树的节点类 |
| 49 | +class SegTreeNode: |
| 50 | + def __init__(self, val=0): |
| 51 | + self.left = -1 # 区间左边界 |
| 52 | + self.right = -1 # 区间右边界 |
| 53 | + self.val = val # 节点值(区间值) |
| 54 | + self.lazy_tag = None # 区间和问题的延迟更新标记 |
| 55 | + |
| 56 | + |
| 57 | +# 线段树类 |
| 58 | +class SegmentTree: |
| 59 | + # 初始化线段树接口 |
| 60 | + def __init__(self, nums, function): |
| 61 | + self.size = len(nums) |
| 62 | + self.tree = [SegTreeNode() for _ in range(4 * self.size)] # 维护 SegTreeNode 数组 |
| 63 | + self.nums = nums # 原始数据 |
| 64 | + self.function = function # function 是一个函数,左右区间的聚合方法 |
| 65 | + if self.size > 0: |
| 66 | + self.__build(0, 0, self.size - 1) |
| 67 | + |
| 68 | + # 单点更新接口:将 nums[i] 更改为 val |
| 69 | + def update_point(self, i, val): |
| 70 | + self.nums[i] = val |
| 71 | + self.__update_point(i, val, 0) |
| 72 | + |
| 73 | + # 区间更新接口:将区间为 [q_left, q_right] 上的所有元素值加上 val |
| 74 | + def update_interval(self, q_left, q_right, val): |
| 75 | + self.__update_interval(q_left, q_right, val, 0) |
| 76 | + |
| 77 | + # 区间查询接口:查询区间为 [q_left, q_right] 的区间值 |
| 78 | + def query_interval(self, q_left, q_right): |
| 79 | + return self.__query_interval(q_left, q_right, 0) |
| 80 | + |
| 81 | + # 获取 nums 数组接口:返回 nums 数组 |
| 82 | + def get_nums(self): |
| 83 | + for i in range(self.size): |
| 84 | + self.nums[i] = self.query_interval(i, i) |
| 85 | + return self.nums |
| 86 | + |
| 87 | + |
| 88 | + # 以下为内部实现方法 |
| 89 | + |
| 90 | + # 构建线段树实现方法:节点的存储下标为 index,节点的区间为 [left, right] |
| 91 | + def __build(self, index, left, right): |
| 92 | + self.tree[index].left = left |
| 93 | + self.tree[index].right = right |
| 94 | + if left == right: # 叶子节点,节点值为对应位置的元素值 |
| 95 | + self.tree[index].val = self.nums[left] |
| 96 | + return |
| 97 | + |
| 98 | + mid = left + (right - left) // 2 # 左右节点划分点 |
| 99 | + left_index = index * 2 + 1 # 左子节点的存储下标 |
| 100 | + right_index = index * 2 + 2 # 右子节点的存储下标 |
| 101 | + self.__build(left_index, left, mid) # 递归创建左子树 |
| 102 | + self.__build(right_index, mid + 1, right) # 递归创建右子树 |
| 103 | + self.__pushup(index) # 向上更新节点的区间值 |
| 104 | + |
| 105 | + # 单点更新实现方法:将 nums[i] 更改为 val,节点的存储下标为 index |
| 106 | + def __update_point(self, i, val, index): |
| 107 | + left = self.tree[index].left |
| 108 | + right = self.tree[index].right |
| 109 | + |
| 110 | + if left == right: |
| 111 | + self.tree[index].val = val # 叶子节点,节点值修改为 val |
| 112 | + return |
| 113 | + |
| 114 | + mid = left + (right - left) // 2 # 左右节点划分点 |
| 115 | + left_index = index * 2 + 1 # 左子节点的存储下标 |
| 116 | + right_index = index * 2 + 2 # 右子节点的存储下标 |
| 117 | + if i <= mid: # 在左子树中更新节点值 |
| 118 | + self.__update_point(i, val, left_index) |
| 119 | + else: # 在右子树中更新节点值 |
| 120 | + self.__update_point(i, val, right_index) |
| 121 | + |
| 122 | + self.__pushup(index) # 向上更新节点的区间值 |
| 123 | + |
| 124 | + # 区间更新实现方法 |
| 125 | + def __update_interval(self, q_left, q_right, val, index): |
| 126 | + left = self.tree[index].left |
| 127 | + right = self.tree[index].right |
| 128 | + |
| 129 | + if left >= q_left and right <= q_right: # 节点所在区间被 [q_left, q_right] 所覆盖 |
| 130 | + if self.tree[index].lazy_tag is not None: |
| 131 | + self.tree[index].lazy_tag += val # 将当前节点的延迟标记增加 val |
| 132 | + else: |
| 133 | + self.tree[index].lazy_tag = val # 将当前节点的延迟标记增加 val |
| 134 | + interval_size = (right - left + 1) # 当前节点所在区间大小 |
| 135 | + self.tree[index].val += val * interval_size # 当前节点所在区间每个元素值增加 val |
| 136 | + return |
| 137 | + |
| 138 | + if right < q_left or left > q_right: # 节点所在区间与 [q_left, q_right] 无关 |
| 139 | + return |
| 140 | + |
| 141 | + self.__pushdown(index) # 向下更新节点的区间值 |
| 142 | + |
| 143 | + mid = left + (right - left) // 2 # 左右节点划分点 |
| 144 | + left_index = index * 2 + 1 # 左子节点的存储下标 |
| 145 | + right_index = index * 2 + 2 # 右子节点的存储下标 |
| 146 | + if q_left <= mid: # 在左子树中更新区间值 |
| 147 | + self.__update_interval(q_left, q_right, val, left_index) |
| 148 | + if q_right > mid: # 在右子树中更新区间值 |
| 149 | + self.__update_interval(q_left, q_right, val, right_index) |
| 150 | + |
| 151 | + self.__pushup(index) # 向上更新节点的区间值 |
| 152 | + |
| 153 | + # 区间查询实现方法:在线段树中搜索区间为 [q_left, q_right] 的区间值 |
| 154 | + def __query_interval(self, q_left, q_right, index): |
| 155 | + left = self.tree[index].left |
| 156 | + right = self.tree[index].right |
| 157 | + |
| 158 | + if left >= q_left and right <= q_right: # 节点所在区间被 [q_left, q_right] 所覆盖 |
| 159 | + return self.tree[index].val # 直接返回节点值 |
| 160 | + if right < q_left or left > q_right: # 节点所在区间与 [q_left, q_right] 无关 |
| 161 | + return 0 |
| 162 | + |
| 163 | + self.__pushdown(index) |
| 164 | + |
| 165 | + mid = left + (right - left) // 2 # 左右节点划分点 |
| 166 | + left_index = index * 2 + 1 # 左子节点的存储下标 |
| 167 | + right_index = index * 2 + 2 # 右子节点的存储下标 |
| 168 | + res_left = 0 # 左子树查询结果 |
| 169 | + res_right = 0 # 右子树查询结果 |
| 170 | + if q_left <= mid: # 在左子树中查询 |
| 171 | + res_left = self.__query_interval(q_left, q_right, left_index) |
| 172 | + if q_right > mid: # 在右子树中查询 |
| 173 | + res_right = self.__query_interval(q_left, q_right, right_index) |
| 174 | + |
| 175 | + return self.function(res_left, res_right) # 返回左右子树元素值的聚合计算结果 |
| 176 | + |
| 177 | + # 向上更新实现方法:更新下标为 index 的节点区间值 等于 该节点左右子节点元素值的聚合计算结果 |
| 178 | + def __pushup(self, index): |
| 179 | + left_index = index * 2 + 1 # 左子节点的存储下标 |
| 180 | + right_index = index * 2 + 2 # 右子节点的存储下标 |
| 181 | + self.tree[index].val = self.function(self.tree[left_index].val, self.tree[right_index].val) |
| 182 | + |
| 183 | + # 向下更新实现方法:更新下标为 index 的节点所在区间的左右子节点的值和懒惰标记 |
| 184 | + def __pushdown(self, index): |
| 185 | + lazy_tag = self.tree[index].lazy_tag |
| 186 | + if lazy_tag is None: |
| 187 | + return |
| 188 | + |
| 189 | + left_index = index * 2 + 1 # 左子节点的存储下标 |
| 190 | + right_index = index * 2 + 2 # 右子节点的存储下标 |
| 191 | + |
| 192 | + if self.tree[left_index].lazy_tag is not None: |
| 193 | + self.tree[left_index].lazy_tag += lazy_tag # 更新左子节点懒惰标记 |
| 194 | + else: |
| 195 | + self.tree[left_index].lazy_tag = lazy_tag |
| 196 | + left_size = (self.tree[left_index].right - self.tree[left_index].left + 1) |
| 197 | + self.tree[left_index].val += lazy_tag * left_size # 左子节点每个元素值增加 lazy_tag |
| 198 | + |
| 199 | + if self.tree[right_index].lazy_tag is not None: |
| 200 | + self.tree[right_index].lazy_tag += lazy_tag # 更新右子节点懒惰标记 |
| 201 | + else: |
| 202 | + self.tree[right_index].lazy_tag = lazy_tag |
| 203 | + right_size = (self.tree[right_index].right - self.tree[right_index].left + 1) |
| 204 | + self.tree[right_index].val += lazy_tag * right_size # 右子节点每个元素值增加 lazy_tag |
| 205 | + |
| 206 | + self.tree[index].lazy_tag = None # 更新当前节点的懒惰标记 |
| 207 | + |
| 208 | + |
| 209 | +class Solution: |
| 210 | + def corpFlightBookings(self, bookings: List[List[int]], n: int) -> List[int]: |
| 211 | + nums = [0 for _ in range(n)] |
| 212 | + self.STree = SegmentTree(nums, lambda x, y: x + y) |
| 213 | + for booking in bookings: |
| 214 | + self.STree.update_interval(booking[0] - 1, booking[1] - 1, booking[2]) |
| 215 | + |
| 216 | + return self.STree.get_nums() |
46 | 217 | ```
|
47 | 218 |
|
0 commit comments