一、学习视频链接
【STUACM-算法入门-前缀和与差分(含二维)】 https://www.bilibili.com/video/BV1pi4y1j7si/?share_source=copy_web&vd_source=dc0e55cfae3b304619670a78444fd795
二、代码跟练
1.一维前缀和代码
def get_sum(l,r):
global sum
return sum[r]-sum[l-1] if l else sum[r]
# l != 0 sum[r]-sum[l-1]
# l == 0 sum[r]
arr = [1,3,7,5,2]
n = len(arr)
sum = [0] * n
sum[0] = arr[0] #i = 0单独初始化
for i in range(1,n):
sum[i] = sum[i-1] + arr[i] #存入前缀和数组
print(sum)
print(get_sum(2,4))
print(get_sum(0,3))
print(get_sum(3,4))
在写这个代码的时候有种很熟悉的感觉,然后发现1480. 一维数组的动态和 这道题就是求前缀和数组的写法,该题题解文章在《http://t.csdnimg.cn/b0veH》第二题。
2.一维差分代码
我先自己写了一遍,代码如下:
def add(l,r,v):
global d
global n
d[l] += v
if r+1 <= n-1: #r+1会越界
d[r+1] -= v
arr = [1,3,7,5,2]
n = len(arr)
sum = [0] * n #前缀和数组
d = [0] * n #差分数组
d[0] = arr[0] #i = 0单独初始化
for i in range(1,n):
d[i] = arr[i] - arr[i-1] #存入差分数组
print("原数组为:\n",arr)
print("差分数组为:\n",d)
add(2,4,5) # “多次操作”
add(1,3,2)
add(0,2,-3)
sum[0] = d[0] #i = 0单独初始化
for i in range(1,n):
sum[i] = sum[i-1] + d[i] #求差分数组的前缀和数组,“一次询问”
print("数组经变换后:\n",sum)
print("此时差分数组为:\n",d)
然后看了up的代码,发现自己写的代码还是有许多可以修改的地方,修改代码如下:
def add(l,r,v):
global d
global n
d[l] += v
d[r+1] -= v
arr = [1,3,7,5,2]
n = len(arr)
d = [0] * (n+1) #差分数组,多一位避免越界,减少需要用来判断的时间
print("原数组为:\n",arr)
print("差分数组为:\n",d)
add(2,4,5) #“多次操作”
add(1,3,2)
add(0,2,-3)
for i in range(1,n):
d[i] += d[i-1] #对差分数组的前缀和操作
for i in range(n):
arr[i] += d[i] #映射回原数组,“一次询问”
print("此时差分数组为:\n",d)
d = [0] * (n+1) #差分数组重置
print("数组经变换后:\n",arr)
修改的部分为:1.将差分数组长度加一;2.去掉前缀和数组,直接对差分数组操作;3.去掉将原数组映射到差分数组的过程,先进行操作,再向原数组映射即可;4.增加了在差分数组映射回原数组后将差分数组初始化(全置为0)的操作,便于后续增加操作。
2024.3.10续:
(题外话:在写代码时,ctrl+alt+L可以自动调整代码格式)
3.二维前缀和代码
我将两种写法都写了一下,代码如下:
调用部分如下(此非完整代码,下面会粘贴完整代码。因为博主将代码写在一起的,所以有点长,先分开看一下。):
arr = [[1, 5, 6, 8],
[9, 6, 7, 3],
[5, 3, 2, 4]]
m, n = 3, 4
# 多开辟一行一列,便捷算法
sum1 = [[0] * (n + 1) for _ in range(m + 1)]
print(add_sum1(sum1))
print(get_sum1(sum1, 1, 1, 2, 2))
# 原始算法,不多开辟空间
sum2 = [[0] * (n) for _ in range(m)]
print(add_sum2(sum2))
print(get_sum2(sum2, 1, 1, 2, 2))
两种写法计算函数如下:
(1)多开辟一行一列,便捷算法
def add_sum1(sum):
global m
global n
global arr
for i in range(1, m + 1):
for j in range(1, n + 1):
sum[i][j] = arr[i - 1][j - 1] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][
j - 1] # 这里本来应该是arr[i][j],但是sum存储i,j是从(1,1)开始的
return sum
def get_sum1(sum, x1, y1, x2, y2):
return sum[x2 + 1][y2 + 1] - sum[x1][y2 + 1] - sum[x2 + 1][y1] + sum[x1][y1]
(2) 原始算法,不多开辟空间
def add_sum2(sum):
global m
global n
global arr
sum[0][0] = arr[0][0]
for i in range(1, m):
sum[i][0] = sum[i - 1][0] + arr[i][0]
for j in range(1, n):
sum[0][j] = sum[0][j - 1] + arr[0][j]
for i in range(1, m):
for j in range(1, n):
sum[i][j] = arr[i][j] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1]
return sum
def get_sum2(sum, x1, y1, x2, y2):
if x1 == 0 and x2 == 0: # 注意特殊判断
return sum[x2][y2]
if x1 == 0:
return sum[x2][y2] - sum[x2][y1 - 1]
if y1 == 0:
return sum[x2][y2] - sum[x1 - 1][y2]
return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1]
完整代码如下:
def add_sum1(sum):
global m
global n
global arr
for i in range(1, m + 1):
for j in range(1, n + 1):
sum[i][j] = arr[i - 1][j - 1] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][
j - 1] # 这里本来应该是arr[i][j],但是sum存储i,j是从(1,1)开始的
return sum
def get_sum1(sum, x1, y1, x2, y2):
return sum[x2 + 1][y2 + 1] - sum[x1][y2 + 1] - sum[x2 + 1][y1] + sum[x1][y1]
def add_sum2(sum):
global m
global n
global arr
sum[0][0] = arr[0][0]
for i in range(1, m):
sum[i][0] = sum[i - 1][0] + arr[i][0]
for j in range(1, n):
sum[0][j] = sum[0][j - 1] + arr[0][j]
for i in range(1, m):
for j in range(1, n):
sum[i][j] = arr[i][j] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1]
return sum
def get_sum2(sum, x1, y1, x2, y2):
if x1 == 0 and x2 == 0: # 注意特殊判断
return sum[x2][y2]
if x1 == 0:
return sum[x2][y2] - sum[x2][y1 - 1]
if y1 == 0:
return sum[x2][y2] - sum[x1 - 1][y2]
return sum[x2][y2] - sum[x1 - 1][y2] - sum[x2][y1 - 1] + sum[x1 - 1][y1 - 1]
arr = [[1, 5, 6, 8],
[9, 6, 7, 3],
[5, 3, 2, 4]]
m, n = 3, 4
# 多开辟一行一列,便捷算法
sum1 = [[0] * (n + 1) for _ in range(m + 1)]
print(add_sum1(sum1))
print(get_sum1(sum1, 1, 1, 2, 2))
# 原始算法,不多开辟空间
sum2 = [[0] * (n) for _ in range(m)]
print(add_sum2(sum2))
print(get_sum2(sum2, 1, 1, 2, 2))
4.二维差分代码
博主在写到这里时发现,如果在求前缀和的时候预留前面(上面和左边)的一行一列,又在求差分数组的时候为了方便又预留后面(下面和右边)一行一列,那空位有点太多了。如果有限制的话,选一个地方预留即可。
下面的代码是只预留右边的一列和下面的一行的写法:
def get_diff(x1,y1,x2,y2,v):
global diff
diff[x1][y1] += v
diff[x1][y2+1] -= v
diff[x2+1][y1] -= v
diff[x2+1][y2+1] += v
arr = [[1, 5, 6, 8],
[9, 6, 7, 3],
[5, 3, 2, 4]]
m, n = 3, 4
diff = [[0] * (n+1) for _ in range(m+1)] #预留右边的一列,下面的一行,防止+1越界
get_diff(0,0,2,1,3)
get_diff(1,1,2,2,-1)
for i in range(m): #求差分数组前缀和
for j in range(n):
if i == 0 and j == 0:
continue
if i == 0:
diff[i][j] += diff[i][j-1]
continue
if j == 0:
diff[i][j] += diff[i-1][j]
continue
diff[i][j] += (diff[i-1][j] + diff[i][j-1] - diff[i-1][j-1])
for i in range(m):
for j in range(n):
arr[i][j] += diff[i][j]
print(arr)
感谢你看到这里!一起加油吧!