2024.3.(6,10号)前缀和与差分算法学习

一、学习视频链接

【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)

感谢你看到这里!一起加油吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值