📅  最后修改于: 2023-12-03 15:28:49.547000             🧑  作者: Mango
在编程中,我们可能会需要多次求解一个区间内所有数的和,或者更新这个区间内的除数,这时候我们可以通过预处理来解决这个问题。下面将介绍两种常见的预处理方法。
前缀和的思想是将原序列前缀的和进行预处理,然后可以在 O(1) 的时间内求得任意区间内的和。
def prefix_sum(nums):
n = len(nums)
prefix_sum = [0] * (n + 1) # 注意下标从1开始
for i in range(1, n+1):
prefix_sum[i] = prefix_sum[i-1] + nums[i-1]
return prefix_sum
def sum_range(prefix_sum, left, right):
return prefix_sum[right+1] - prefix_sum[left]
该方法的时间复杂度是 $O(n)$,只需要一次预处理和O(1)的查询即可。
线段树是一种二叉树结构,其每个节点表示一个区间。该结构可以较快地解决区间查询和区间更新问题。
class SegmentTree:
def __init__(self, nums):
self.n = len(nums)
self.tree = [0] * (self.n * 4) # 定义4倍空间的线段树数组
self.build(1, 0, self.n-1, nums)
def build(self, node, start, end, nums):
if start == end:
self.tree[node] = nums[start]
else:
mid = (start + end) // 2
left = 2 * node
right = 2 * node + 1
self.build(left, start, mid, nums)
self.build(right, mid+1, end, nums)
self.tree[node] = self.tree[left] + self.tree[right]
def sum_range(self, node, start, end, left, right):
if left <= start and end <= right:
return self.tree[node]
if right < start or end < left:
return 0
mid = (start + end) // 2
left_node = 2 * node
right_node = 2 * node + 1
return self.sum_range(left_node, start, mid, left, right) + \
self.sum_range(right_node, mid+1, end, left, right)
def update(self, node, start, end, index, value):
if start == end:
self.tree[node] = value
else:
mid = (start + end) // 2
left_node = 2 * node
right_node = 2 * node + 1
if index <= mid:
self.update(left_node, start, mid, index, value)
else:
self.update(right_node, mid+1, end, index, value)
self.tree[node] = self.tree[left_node] + self.tree[right_node]
该方法的时间复杂度为 $O(log_2 n)$,需要一次预处理和O(logn)的查询和更新操作即可。
两种方法各有优缺点,针对不同的问题可以根据实际情况选择适合的方法。