📅  最后修改于: 2023-12-03 14:57:11.999000             🧑  作者: Mango
范围总和查询和平方根更新是一种常见的问题,主要用于计算一个给定范围内所有数的总和,以及在该范围内更新单个数的值。
该问题的解决方案有多种,包括暴力算法、分治算法和动态规划算法等。在本文中,我们将介绍两种最常用的解决方案:前缀和算法和线段树算法。
前缀和算法是一种基于数组的算法,用于在常数时间内计算一个给定范围内所有数的总和。它的时间复杂度为O(1),空间复杂度为O(n),其中n表示数组的大小。
我们可以用一个新的数组sum来存储原数组的前缀和。sum[i]表示从0到i的所有元素的总和。则对于任何范围[i,j],我们只需要计算sum[j]-sum[i-1]就可以得到该范围内所有元素的总和了。
代码片段如下:
def prefix_sum(arr):
n = len(arr)
sum = [0]*n
sum[0] = arr[0]
for i in range(1,n):
sum[i] = sum[i-1] + arr[i]
return sum
def range_sum_query(sum, i, j):
if i==0:
return sum[j]
else:
return sum[j]-sum[i-1]
线段树是一种基于树的算法,用于在O(logn)时间内计算一个给定范围内所有数的总和。它的空间复杂度为O(n),其中n仍然表示数组的大小。
线段树的基本思想是将一个数组分成若干个区间,并用一棵二叉树表示这些区间。每个节点表示一个区间,其左子树和右子树分别代表该区间的左半部分和右半部分。每个节点存储着该区间内所有元素的总和。
代码片段如下:
class SegmentTree:
def __init__(self, arr):
self.n = len(arr)
self.tree = [0]*(4*self.n)
self.build_tree(arr, 0, self.n-1, 0)
def build_tree(self, arr, l, r, idx):
if l==r:
self.tree[idx] = arr[l]
else:
mid = (l+r)//2
self.build_tree(arr, l, mid, 2*idx+1)
self.build_tree(arr, mid+1, r, 2*idx+2)
self.tree[idx] = self.tree[2*idx+1] + self.tree[2*idx+2]
def update(self, i, val):
self.update_tree(0, self.n-1, i, val, 0)
def update_tree(self, l, r, i, val, idx):
if l==r:
self.tree[idx] = val
else:
mid = (l+r)//2
if i<=mid:
self.update_tree(l, mid, i, val, 2*idx+1)
else:
self.update_tree(mid+1, r, i, val, 2*idx+2)
self.tree[idx] = self.tree[2*idx+1] + self.tree[2*idx+2]
def range_sum_query(self, i, j):
return self.query_tree(0, self.n-1, i, j, 0)
def query_tree(self, l, r, i, j, idx):
if l>j or r<i:
return 0
elif l>=i and r<=j:
return self.tree[idx]
else:
mid = (l+r)//2
return self.query_tree(l, mid, i, j, 2*idx+1) + self.query_tree(mid+1, r, i, j, 2*idx+2)
前缀和算法和线段树算法是范围总和查询和平方根更新问题的两种最常用解决方案。前者适用于对内存空间要求较低的场景,而后者则适用于需要更快查询速度的场景。在实际应用中,我们应该根据实际情况选择合适的算法。