Skip to content

Commit 934b009

Browse files
author
杨世超
committed
Update 1109. 航班预订统计.md
1 parent 0034d80 commit 934b009

File tree

1 file changed

+176
-5
lines changed

1 file changed

+176
-5
lines changed

Solutions/1109. 航班预订统计.md

Lines changed: 176 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,186 @@
3333

3434
## 解题思路
3535

36-
### 思路 1:
36+
### 思路 1:线段树
3737

38+
- 初始化一个长度为 `n`,值全为 `0``nums` 数组。
39+
- 然后根据 `nums` 数组构建一棵线段树。每个线段树的节点类存储当前区间的左右边界和该区间的和。并且线段树使用延迟标记。
40+
- 然后遍历三元组操作,进行区间累加运算。
41+
- 最后从线段树中查询数组所有元素,返回该数组即可。
3842

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

40-
## 代码
41-
42-
### 思路 1 代码:
45+
### 思路 1 线段树代码:
4346

4447
```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()
46217
```
47218

0 commit comments

Comments
 (0)