算法:差分数组

今天学到了一个新知识,差分数组,参考灵神笔记

举例
考虑数组   a = [ 1 , 3 , 3 , 5 , 8 ] \ a = [1,3,3,5,8]  a=[1,3,3,5,8],对其中的相邻元素两两作差(右边减左边),得到数组   [ 2 , 0 , 2 , 3 ] \ [2,0,2,3]  [2,0,2,3]。然后在开头补上   a [ 0 ] \ a[0]  a[0],得到差分数组:
d = [ 1 , 2 , 0 , 2 , 3 ] d = [1,2,0,2,3] d=[1,2,0,2,3]
这有什么用呢?如果从左到右累加   d \ d  d 中的元素,我们就「还原」回了   a \ a  a 数组   [ 1 , 3 , 3 , 5 , 8 ] \ [1,3,3,5,8]  [1,3,3,5,8]
这又有什么用呢?现在把连续子数组   a [ 1 ] , a [ 2 ] , a [ 3 ] \ a[1],a[2], a[3]  a[1]a[2]a[3] 都加上 10,得到   a ′ = [ 1 , 13 , 13 , 15 , 8 ] \ a^{\prime}=[1,13,13,15,8]  a=[1,13,13,15,8]。再次两两作差,并在开头补上   a ′ [ 0 ] \ a^{\prime}[0]  a[0],得到差分数组:
d ′ = [ 1 , 12 , 0 , 2 , − 7 ] d^{\prime} = [1,12,0,2,-7] d=[1,12,0,2,7]
对比   d \ d  d   d ′ \ d^{\prime}  d,你会发现,对   a \ a  a 中连续子数组的操作,可以转变成对差分数组   d \ d  d 中两个数的操作。

定义和性质
对于数组   a \ a  a,定义其差分数组(difference array)为
d [ i ] = { a [ 0 ] , i = 0 a [ i ] − a [ i − 1 ] , i ≥ 1 d[i] = \begin{cases} a[0],&i=0 \\ a[i] - a[i - 1],&i\geq1 \end{cases} d[i]={a[0],a[i]a[i1],i=0i1
性质1:从左到右累加   d \ d  d 中的元素,可以得到数组   a \ a  a
性质2:如下两个操作是等价的。

  • 区间操作:把   a \ a  a 的子数组   a [ i ] , a [ i + 1 ] , ⋅ ⋅ ⋅ , a [ j ] \ a[i],a[i+1],···,a[j]  a[i],a[i+1],⋅⋅⋅,a[j] 都加上   x \ x  x
  • 单点操作:把   d [ i ] \ d[i]  d[i] 增加   x \ x  x,把   d [ j + 1 ] \ d[j+1]  d[j+1] 减少   x \ x  x。特别的,如果   j + 1 = n \ j+1=n  j+1=n,则只需把   d [ i ] \ d[i]  d[i] 增加   x \ x  x。(   n \ n  n 作为数组   a \ a  a 的长度)

利用性质 2,我们只需要   O ( 1 ) \ O(1)  O(1) 的时间就可以完成数组   a \ a  a 上的区间操作。最后利用性质 1 从差分数组复原出数组   a \ a  a

JS代码模版

// 你有一个长为 n 的数组 a,一开始所有元素均为 0。
// 给定一些区间操作,其中 queries[i] = [left, right, x],
// 你需要把子数组 a[left], a[left+1], ... a[right] 都加上 x。
// 返回所有操作执行完后的数组 a。
function solve(n, queries) {
    let diff = new Array(n).fill(0); // 差分数组
    for (const q of queries) {
        const left = q[0], right = q[1], x = q[2];
        diff[left] += x;
        if (right + 1 < n) {
            diff[right + 1] -= x;
        }
    }
    for (let i = 1; i < n; i++) {
        diff[i] += diff[i - 1]; // 直接在差分数组上复原数组 a
    }
    return diff;
}

第一次手打这些数学公式,还有点累的啊~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值