本文参考博客:https://blog.csdn.net/qq_31601743/article/details/105352885
问题背景
如果给你一个包含5000万个元素的数组,然后会有频繁区间修改操作,那什么是频繁的区间修改操作呢?比如让第1个数到第1000万个数每个数都加上1,而且这种操作时频繁的。
此时你应该怎么做?很容易想到的是,从第1个数开始遍历,一直遍历到第1000万个数,然后每个数都加上1,如果这种操作很频繁的话,那这种暴力的方法在一些实时的系统中可能就拉跨了。
因此,今天的主角就出现了——差分数组。
算法原型
比如我们现在有一个数组arr,arr={0,2,5,4,9,7,10,0}
那么差分数组是什么呢?其实差分数组本质上也是一个数组,我们暂且定义差分数组为d,差分数组d的大小和原来arr数组大小一样,而且di=arr[i]-arr[i-1],且d[i]=0,它的含义是什么?就是原来数组i位置上的元素和i-1位置上的元素作差,得到的值就是d[i]的值。
所以,例子中的arr数组其对应的差分数组值如下图所示。
那么构造了这么个玩意有什么用呢?难道是来浪费宝贵的内存空间的?嗯,确实是来浪费宝贵的内存了,但是却换了时间上的高效。
现在我们有这么一个区间修改操作,即在区间1~4上,所有的数值都加上3.
我们不要傻傻地遍历arr数组的[1,4]范围,然后再分别给每个值加上3,我们此时更改差分数组d即可。
显而易见,差分数组d在[2,4]范围内的值都不用改变,只需要改变差分数组位置1和位置5的值即可,即d[1]=d[1]+3,而d[5]=d[5]-3,其余不变,为什么呢?因为差分数组的定义——d[i]=arr[i]-arr[i-1]
当更新完差分数组d的值后,原始arr的值可以用差分数组d求得
d[i]=d[i]+d[i-1] //需要更新d[i]的值
arr[i] = d[i]+d[i-1]
即arr[i]=d[i]+d[i-1]+d[i-2]+…+d[0];因为上面的差分数组的值是由arr相邻相减获得(d[i] = arr[i]-arr[i-1])
总结
可以看到,如果需要对[L,R]范围内所有数都进行相同的操作,我们不需要从[L,R]遍历然后在每个值上进行相同操作,只需要在差分数组d中改变L和R+1的值即可。但是在查询arr数组中某个位置的数时,却要根据差分数组从前往后递推求值。
所以,该方法适用于区间频繁修改,而且这个区间范围是比较大的,离线查询的情况。
利用差分数组解题:
题目链接:https://www.dotcpp.com/oj/problem1501.html
```c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int N, M;
int Li, Ri, Ci;
scanf("%d %d", &N, &M);
int* number = (int*)malloc(sizeof(int) * (N+2));
int* cut_array = (int*)malloc(sizeof(int) * (N+2)); //差分数组
memset(number, 0, sizeof(int) * (N+2));
memset(cut_array, 0, sizeof(int) * (N+2));
for (int i = 0; i < M; i++)
{
scanf("%d %d %d", &Li, &Ri, &Ci);
cut_array[Li] += Ci; //更新差分数组的值
cut_array[Ri+1] -= Ci;
}
for (int i = 1; i <= N; i++)
{
//int temp = cut_array[i] + cut_array[i - 1];
//number[i] = temp;
//cut_array[i] = temp;
cut_array[i] = cut_array[i] + cut_array[i - 1];
number[i] = cut_array[i];
printf("%d ", number[i]);
}
printf("\n");
free(number);
free(cut_array);
return 0;
}
本题可以节省一半的空间:只需要记录差分数组的值即可,(因为差分数组d和原始数组arr初始化都为0)
#include<iostream>
using namespace std;
int d[100005]; //d[i]表示第i个小朋友比第i-1个小朋友多的苹果
int main()
{
int n,m,l,r,c;
cin>>n>>m;
while(m--){
cin>>l>>r>>c;
d[l]+=c; //每一次发苹果第l个小朋友比第l-1个小朋友多c个,
d[r+1]-=c; //第r+1个小朋友比第r个小朋友少c个。
}
for(int i=1;i<=n;i++){
d[i]+=d[i-1]; //根据差分数组的性质进行求和
cout<<d[i]<<" ";
}
cout<<endl;
return 0;
}