前缀和与差分数组 (一维和二维)

前言:今天写题目的时候遇到了一个二维差分的题目,由于之前没有接触过,因此前来记录一下


前缀和

差分通常需要和前缀和一起使用才能发挥奇效,所以我们先来介绍一下前缀和。

  • 一维前缀和:对于一个长度为 n n n 的数组 a r r arr arr , 前缀和数组的定义为 s u m [ i + 1 ] = s u m [ i ] + a r r [ i ] sum[i + 1] = sum[i] + arr[i] sum[i+1]=sum[i]+arr[i], 其中 i ∈ [ 0 , n ) i \in [0, n) i[0,n).

  • 二维前缀和:对于一个行数为 m m m 列数为 n n n 的二维数组 m a t mat mat, 定义为 s u m [ i + 1 ] [ j + 1 ] = s u m [ i + 1 ] [ j ] + s u m [ i ] [ j + 1 ] − s u m [ i ] [ j ] + m a t [ i ] [ j ] sum[i + 1][j + 1] = sum[i + 1][j] + sum[i][j + 1] - sum[i][j] + mat[i][j] sum[i+1][j+1]=sum[i+1][j]+sum[i][j+1]sum[i][j]+mat[i][j], 其中 i ∈ [ 0 , m ) , j ∈ [ 0 , n ) i \in [0, m), j \in [0, n) i[0,m),j[0,n).

  • 前缀和的作用:对于求出的一维前缀和,我们可以在 O ( 1 ) O(1) O(1) 的时间复杂度内求出数组中任意子数组的和,对于下标在 [ i , j ] [i, j] [i,j] 的子数组,其和为 s u m [ j + 1 ] − s u m [ i ] sum[j + 1] - sum[i] sum[j+1]sum[i]

  • 映射到二维空间,则可以通过二维的前缀和数组求出任意一个矩形区域对应的和,即原数组中左上角 ( i , j ) (i, j) (i,j) 与右下角 ( x , y ) (x, y) (x,y) 确定的矩形区域,对应的值总和为 s u m [ x + 1 ] [ y + 1 ] − s u m [ x + 1 ] [ j ] − s u m [ i ] [ y + 1 ] + s u m [ i ] [ j ] sum[x + 1][y + 1] - sum[x + 1][j] - sum[i][y + 1] + sum[i][j] sum[x+1][y+1]sum[x+1][j]sum[i][y+1]+sum[i][j]


差分数组

1. 一维差分
  • 差分数组的作用是可以利用 线性时间 来处理 区间修改问题.

  • 例如存在一个二维数组 r e q req req,对于每一个 r = r e q [ i ] r = req[i] r=req[i], 需要将 a r r arr arr 数组中下标在 [ r [ 0 ] , r [ 1 ] ] [r[0], r[1]] [r[0],r[1]] 间的元素都加上 r [ 2 ] r[2] r[2], 那么此时差分数组就具备很好的使用场景,具体的代码模板如下:

void check(vector<vector<int>>& req, vector<int>& arr){
    int m = req.size(), n = arr.size();

    vector<int> diff(n + 1);    

    for(auto& r : req){
        int x = r[2];
        diff[r[0]] += x;
        diff[r[1] + 1] -= x; 
    }

    arr[0] += diff[0];

    for(int i = 1; i < n; ++i){
        diff[i] += diff[i - 1];
        arr[i] += diff[i];
    }
}
  • 此时的 a r r arr arr 数组中便为操作后的值

  • 这里贴出部分题目供大家练习使用: LC 1589. 所有排列中的最大和, LC 1109. 航班预订统计

  • 注意:区间处理 加减 问题也可以使用树状数组或线段树,但是代码量上远远不如差分数组,后两者适用于更高级的使用场景

2. 二维差分
  • 如果你看到这里都毫无压力,那么可以看看接下来的二维差分数组

  • 二维差分无非就是把一维差分的性质映射到了二维平面,适用场景变成了:二维数组中的子矩阵修改问题

  • 举个栗子:对于一个 0 0 0 m ∗ n m*n mn 数组,若想将原数组中 ( i − 1 , j − 1 ) (i - 1, j - 1) (i1,j1) ( x − 1 , y − 1 ) (x - 1, y - 1) (x1,y1) 所确定的矩形中的元素都加上 1 1 1, 那么在差分数组上的操作为下图所示:

在这里插入图片描述

  • 只有对图中四个位置进行操作才能保证作用范围的正确性,对应的代码如下:
// 行数和列数都多开2, 以便前缀和的计算
vector<vector<int>> diff(m + 2, vector<int>(n + 2, 0));  

diff[i][j]++;
diff[x + 1][y + 1]++;
diff[x + 1][j]--;
diff[i][y + 1]--;
  • 最后再利用 O ( m n ) O(mn) O(mn) 的时间做一次二维前缀和即可
for(int i = 1; i <= m; ++i){
    for(int j = 1; j <= n; ++j){
        diff[i][j] += diff[i - 1][j] + diff[i][j - 1] - diff[i - 1][j - 1];
    }
}

总结

一维、二维前缀和以及一维差分是比较常见的,大家记忆后最好转化为适合自己的代码模板,二维差分存在难度,大家可以根据需要自行决定。收!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值