calmsym学习数据结构与算法之 前缀和与差分


前言

	在比赛或者写题中,我们时常遇到需要我们执行区间维护或区间修改的题目
或者是通过这一操作来达成某些功能最终AC题目。
	今天我们来学习前缀和与差分,入门区间维护与查询算法的学习

一、前缀和的原理及实现

1.一维前缀和

现在有一个数列 a[n] = { 1, 2, 3, 4, 5 ,…, n }
第 i 位的前缀和即指从数列下标 1 到 i 的和,我们不妨设数列sum[n]为数列a的前缀和数列,那么满足
s u m [ i ] = ∑ k = 1 i a [ k ] = a [ 1 ] + a [ 2 ] + . . . + a [ i ] sum[i]=\sum_{k=1}^i{a[k]}=a[1]+a[2]+...+a[i] sum[i]=k=1ia[k]=a[1]+a[2]+...+a[i]
代码实现

int a[N],sum[N];
void init(int n){//初始化
    for(int i=1;i<=n;i++){
        read(a[i]);//快读无需在意相当于cin>>a[i];
        sum[i]=sum[i-1]+a[i];
    }
}

前缀和能够以O(1)的时间复杂度查询区间和

设 L 和 R 分别为区间左右端点,欲求区间 [ L , R ] 的区间和怎么办?用前缀和很好解决
我们知道

s u m [ L ] = ∑ k = 1 L a [ k ] s u m [ R ] = ∑ k = 1 R a [ k ] L ≤ R ∴ s u m [ L , R ] = a [ L ] + a [ L + 1 ] + . . . . + a [ R ] = ∑ k = 1 R a [ k ] − ∑ k = 1 L − 1 a [ k ] sum[L]=\sum_{k=1}^L{a[k]}\\ sum[R]=\sum_{k=1}^R{a[k]}\\ L\le R\\ \therefore sum[L,R] =a[L]+a[L+1]+....+a[R]=\sum_{k=1}^Ra[k]-\sum_{k=1}^{L-1}{a[k]} sum[L]=k=1La[k]sum[R]=k=1Ra[k]LRsum[L,R]=a[L]+a[L+1]+....+a[R]=k=1Ra[k]k=1L1a[k]

int l,r;
int search(int l,int r){
	return sum[r]-sum[l-1];
}

2.二维前缀和

我们设二维数组 a[N][N] 和二维前缀和sum[N][N]

那么有
s u m [ 0 ] [ 0 ] = a [ 0 ] [ 0 ] s u m [ 0 ] [ 1 ] = a [ 0 ] [ 0 ] + a [ 0 ] [ 1 ] s u m [ 1 ] [ 0 ] = a [ 0 ] [ 0 ] + a [ 1 ] [ 0 ] s u m [ 1 ] [ 1 ] = a [ 0 ] [ 0 ] + a [ 1 ] [ 0 ] + a [ 0 ] [ 1 ] + a [ 1 ] [ 1 ] sum[0][0]=a[0][0]\\ sum[0][1]=a[0][0]+a[0][1] \\ sum[1][0]=a[0][0]+a[1][0]\\ sum[1][1]=a[0][0]+a[1][0]+a[0][1]+a[1][1] sum[0][0]=a[0][0]sum[0][1]=a[0][0]+a[0][1]sum[1][0]=a[0][0]+a[1][0]sum[1][1]=a[0][0]+a[1][0]+a[0][1]+a[1][1]
我们这里引用一篇知乎@朝夕 的图来更好的理解二维前缀和
在这里插入图片描述
所以化简后我们知道
i行j列的前缀和=少一行或少一列的矩阵前缀和之和 - 重叠部分矩阵前缀和 + 当前坐标的值

s u m [ i ] [ j ] = s u m [ i − 1 ] [ j ] + s u m [ i ] [ j − 1 ] − s u m [ i − 1 ] [ j − 1 ] + a [ i ] [ j ] sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j] sum[i][j]=sum[i1][j]+sum[i][j1]sum[i1][j1]+a[i][j]

for(int y=1;y<=n;y++)//n行
    for(int x=1;x<=m;x++)//m列
    {
        if(x==1&&y==1) b[y][x]=a[y][x];//左上角的值
        else if(x==1) b[y][x]=b[y-1][x]+a[y][x];//第一列
        else if(y==1) b[y][x]=b[y][x-1]+a[y][x];//第一行
        else b[y][x]=b[y-1][x]+b[y][x-1]-b[y-1][x-1]+a[y][x];
    }

二、差分的原理及实现

1.差分

类似于数学中的求导和积分,差分可以看成前缀和的逆运算

前缀和是从1->当前下标的所有元素的和
而差分则是记录坐标 i 与 i-1 的差

所以

a [ i ] = d [ 1 ] + . . . . + d [ i ] a[i]=d[1]+....+d[i] a[i]=d[1]+....+d[i]
用下面这张图可以很直观的看出差分与原数组之间的关系

在这里插入图片描述

设d[N]为数组a[N]的前缀和数组
d [ i ] = a [ i ] − a [ i − 1 ] d[i]=a[i]-a[i-1] d[i]=a[i]a[i1]

2.差分有什么用?怎么实现?

差分是一种处理数据的巧妙而简单的方法,它应用于区间的修改和询问问题

如何修改?为什么能这样修改?
证明: 我们知道区间修改 加值或减值 并不会影响区间内相邻两数的差值

每次操作只需要修改区间 [ L , R ] 的两个端点的D [ ] 值,复杂度是O ( 1 ) 的。经过这种操作后,原来直接在a [ ] 上做的复杂度为O ( n ) 的区间修改操作,就变成了在D [ ]上做的复杂度为O ( 1 ) 的端点操作。
利用D [ ] ,能精确地实现只修改区间内元素的目的,而不会修改区间外的a [ ] 值。因为前缀和
a [ x ] = D [ 1 ] + D [ 2 ] + . . . + D [ x ] a [ x ] = D [ 1 ] + D [ 2 ] + . . . + D [ x ] a[x]=D[1]+D[2]+...+D[x]
有:
(1)1 ≤ x < L ,前缀和a [ x ]不变;
(2)L ≤ x ≤ R,前缀和a [ x ] 增加了d;
(3)R < x ≤ N,前缀和a [ x ]不变,因为被D [ R + 1 ] 中减去的 d 抵消了。

总结

前缀和与差分是我们初入区间维护,修改,查询这类操作的第一门算法
前缀和与差分对于区间查询和区间修改的效率很高,但是对于单点查询,他有其局限性,在后面我们还会学习树状数组线段树等数据结构与算法进一步深入学习区间维护的操作方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值