前缀和 & 差分

前缀和 & 差分 基于容斥思想。

所谓容斥,就是为了解决不重不漏的计算,容斥的做法是:

先不管重复全部计算,再逐个将重复计算的内容剔除。

举个简单的例子:已知宽高,求正方形的周长。

1、一维数组前缀和

已知数组 nums, 定义前缀和数组 sum,使得 i ∈ [ 0, num.length-1 ] , 有 sum[i] 等于 nums中 0 到 i 的数据和。

/*
    思路:
    (1) sum[0] = nums[0]
    (2) sum[i] = sum[i-1] + nums[i]
*/
function fn(nums) {
    let sum = [];
    sum[0] = nums[0];
    for(let i=1;i<nums.length;i++) sum[i] = sum[i-1] + nums[i];
    return sum;
}

// test case
let nums = [1,2,3,4,5];
let res = fn(nums);
console.log(res);
// [ 1, 3, 6, 10, 15 ]

应用,求区间和: 开区间、左开右闭、左闭右开、闭区间

下面以区间 [2, 4] 为例:

// (2,4) -> 4
console.log( sum[4-1] - sum[2] );

// (2,4] -> 9
console.log( sum[4] - sum[2] );

// [2, 4) -> 7
console.log( sum[4-1] - sum[2-1] );

// [2,4] -> 12
console.log( sum[4] - sum[2-1] );

2、二维数组前缀和

已知M*N矩阵 nums, 定义前缀数组 sum,使得

x ∈ [0, m) , y ∈ [0,n)
在这里插入图片描述

/*
    思路:
    (1) 在 0,0 处   sum[0][0] = nums[0][0]
    (2) 在 x=0 处   sum[x][y] = sum[x][y-1] + nums[x][y];  无需考虑容斥
    (3) 在 y=0 处   sum[x][y] = sum[x-1][y] + nums[x][y];  无需考虑容斥
    (4) 其他        sum[x][y] = sum[x-1][y] + sum[x][y-1] + nums[x][y] - sum[x-1][y-1]; 
*/
function fn(nums) {
    let m = nums.length;
    let n = nums[0].length;
    let sum = new Array(m).fill(0).map(v => new Array(n).fill(0));
    for(let x=0;x<m;x++) {
        for(let y=0;y<n;y++) {
            if(x===0 && y===0) sum[x][y] = nums[x][y];
            if(x===0 && y!==0) sum[x][y] = sum[x][y-1] + nums[x][y];
            if(x!==0 && y===0) sum[x][y] = sum[x-1][y] + nums[x][y];
            if(x!==0 && y!==0) sum[x][y] = sum[x-1][y] + sum[x][y-1] + nums[x][y] - sum[x-1][y-1];
        }
    }
    return sum;
}

//  test case
let nums = 
[
    [1,2,4,3],
    [5,1,2,4],
    [6,3,5,9]
];

let sum = fn(nums);
console.log(sum);
// [ [ 1, 3, 7, 10 ], [ 6, 9, 15, 22 ], [ 12, 18, 29, 45 ] ]

3、差分数组

差分就是求前缀和的逆运算,已知数组 nums,定义差分数组 diff,使得

i ∈ [ 0, num.length-1 ]

diff[0] = nums[0];

diff[i] = nums[i] - nums[i-1];

可知,有以下性质:

(1)nums[i] 是 diff[i] 的前缀和
(2)可以使用差分来计算nums每项的前缀和(难以理解,为什么要走弯路用差分去求原数组前缀和???先记下来吧)
在这里插入图片描述
由 nums 求差分数组 diff :

function fn(nums) {
    let diff = [];
    diff[0] = nums[0];
    for(let i=1;i<nums.length;i++) diff[i] = nums[i] - nums[i-1];
    return diff;
}

// test case:
let nums = [1,2,3,4,5];
let diff = fn(nums);
console.log(diff);
// [ 1, 1, 1, 1, 1 ]

应用:快速处理区间加减操作

在这里插入图片描述
第一行为 原数组 nums

第二行为 差分数组 diff

可以从上面的图示观察到,某段闭区间 [ L, R ] 中每个元素都加 k 的话

只需维护差分数组,diff[ L ] += kdiff[ R+1 ] -= k

因此,到这里,其实我们就能明白为什么要走弯路用差分数组求原数组,用差分数组求前缀和了,毕竟我们在频繁的区间加减后,只维护了差分数组,而不维护数组。

示例:

1893. 检查是否区域内所有整数都被覆盖

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值