算法知识之差分数组(一维)

本文介绍了差分数组的概念及其在处理区间修改问题上的应用。通过举例说明,数组在经过多次加减操作后的更新,可以使用差分数组在O(m+n)的时间复杂度内完成,显著提高了效率。文章提供了代码模板展示如何实现这一优化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大家在看我这篇文章之前可以先看一下前缀和的概念

算法基础之前缀和

 

        我们先了解一下差分数组解决什么问题:当你有一个数组,你想要进行m次询问,每次询问为在区间内的每一位加上或减去一个值(即[l, r] + value或[l, r] - value),返回最终询问过后的数组,可能有点抽象,那就举个例子进行理解吧。

        首先有一个数组,还按之前的前缀和的数组来说吧,arr = [1, 5, 8, 2, 13, 6],我们对其进行3次询问,第一次询问是在下标为[1, 4]的区间内的每个值+5(结果是arr = [1, 10, 13, 7, 18, 6]),第二次询问是在下标为[3, 5]的区间内的每个值+1(结果是arr = [1, 10, 13, 8, 19, 7]),第三次询问是在下标为[0, 2]的区间内的每个值-3(结果是arr = [-2, 7, 10, 8, 19, 7]),我们首先想到的无非就是暴力做法,也就是对每次询问都对询问内的区间进行枚举,然后进行相应的加减运算,当然,询问次数少完全可以这样做,但如果对其询问的次数多了呢?如果数组的长度为n, 进行m次询问的话,这样的最差时间复杂度为O(m * n),还是挺高的,容易造成超时,我们此刻就可以对其进行优化

        我们观察一下解决的问题,因为我们最终想要得到的是一个更改后数组的结果,所以我们根本不用知道更改过程之间的数组内的每个值,因此就要引入差分数组

        我们用D来表示差分数组,当I = 0时,D[i] = arr[i],当I > 0时,D[i] = arr[i] – arr[I – 1].根据这个公式,原先arr的差分数组可以表示为D = [1, 4, 3, -6, 11, -7],仔细观察不难发现,差分数组其实就是前缀和的逆运算,我们反观差分数组D,arr[i] = D[0] + D[1] + D[2]+……+D[i]。

        我们利用的其实就是这个性质,先上公式:当我们想要修改数组中区间的时候,[l. r] + value 等价于D[l] + value, D[r + 1] – value。

        我们对其仍然进行以上三次操作,第一次询问[1, 4]结果为D = [1, 9, 3, -6, 11, -12],第二次询问[3, 5],诶?此刻不难发现,在D[r + 1]上数组越界了,怎么办?其实在这里有两种解决方案,一是再额外对差分数组开辟一块内存空间来存放-value,二是不用管他,不写D[r + 1]即可,也就是在这里我们写成D = [1, 9, 3, -5, 11, -12, -1]和D = [1, 9, 3, -5, 11, -12]均可,自己可以自行实现一下证明一下是否一样,这里我们采用第二种写法,第三次询问[0, 2]的结果为D = [-2, 9, 3, -2, 11, -12],最终的差分数组修改完了,在前面我们知道差分数组就是原数组的前缀和的逆运算,我们对其求每一项的前缀和得到最终的数组D = [-2, 7, 10, 8, 19, 7]可以看到和上文通过枚举求得的最终数组arr是一样的,这就是差分数组,时间复杂度为O(m + n),适合多次区间操作一次数组询问。

下面是代码模板:

#include<bits/stdc++.h>
using namespace std;
//定义差分数组,这里我们多定义一位,在程序上多消耗一个存储空间可以减少一次
//if语句的判断,来加快运行时间(判断临界的时候)
vector<int>D(7, 0);
void add(int l, int r, int val) {
    D[l] += val;
    D[r + 1] -= val;
}
int main()
{
    vector<int>arr = {1, 5, 8, 2, 13, 6};
    //对差分数组进行初始化
    for (int i = 0; i < 6; i++) {
        if (i == 0)
            D[i] = arr[i];
        else
            D[i] = arr[i] - arr[i - 1];
    }
    //对区间进行加减操作
    add(1, 4, 5);
    add(3, 5, 1);
    add(0, 2, -3);
    //求得差分数组的每一项的前缀和
    for (int i = 1; i < 6; i++) {
        D[i] += D[i - 1];
    }
    //输出结果
    for (int i = 0; i < 6; i++) {
        cout << D[i] << " ";
    }

    system("pause");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值