差分数组是较为常见的算法,该数组存储着原数组中元素与前一个元素的差值,由于原数组中下标为0的元素没有前一个元素,因此前缀和数组中下标为0的元素为0。
差分数组一般是用于题目要求多次对原数组进行不同指定范围加减操作的情况,比如说将原数组中i到j下标的所有元素都加上10。
根据原数组生成差分数组也比较容易,从下标1开始遍历原数组,将此刻遍历的元素的值减去上一个元素的值,便可得到差分数组中相同下标下的元素值,那么如何对原数组进行指定范围的加减操作呢?
当对原数组中某个指定范围的元素进行加操作,设该范围为i到j,原数组为arr,差分数组为diff,加减的值为x。
未操作前diff[i]储存着的值是arr[i]-arr[i-1],操作后,由于arr[i]加了x,arr[i-1]没有加x,则arr[i]与arr[i-1]的差值变化了x(x可正可负,所以差值可能变大也可能变小),所以diff[i]就变成了arr[i]-arr[i-1]+x,而diff[i]=arr[i]-arr[i-1],所以可得公式。
新diff[i]=旧diff[i]+x
讨论完diff[i]的情况,再来看看范围内的元素差值是否有变化,这里假设j>i,以diff[i+1],那么未操作前diff[i+1]=arr[i+1]-arr[i],操作后,arr[i+1]和arr[i]都加了x,新diff[i]=(arr[i+1]+x)-(arr[i]+x),去除括号后两个x可以相互抵消,故可得公式。
新diff[i+1]=旧diff[i+1]
由公式可知指定范围内的元素的差值不会改变。
最后再来看下diff[j+1],diff[j+1]比较特殊,它还要考虑是否会越界的问题,这里假设j+1小于数组长度,操作前,diff[j+1]=arr[j+1]-arr[j],操作后,arr[j]加了x,arr[j+1]没变,新diff[j+1]=arr[j+1]-(arr[j]+x),化简后可得公式。
新diff[j+1]=旧diff[j+1]-x
前面提到过原数组中第一个元素前面无元素,那么在差分数组下标为0的位置应该存储什么?考虑一种边界情况,当需要进行数值操作的范围起始点i为0时,我们只需在arr[0]进行操作就行,因为对i到j范围的操作可以拆分为单独对i和对i+1到j下标的原数组的两个操作,但是为了操作统一,不对i==0的情况做特殊处理,可以让diff[0]存储arr[0]的值,这样就操作diff[0]就相当于直接操作arr[0]了。
讨论完进行指定范围操作的所有情况后,就需要考虑一个问题了,如何获取完成全部数值操作后的数组arr?
可以将diff[0]直接赋值给arr[0],原因可见上文,这里不再赘述。
diff[i]存储着arr[i]-arr[i-1]的差值,也就是说diff[i]=arr[i]-arr[i-1],要求的值是arr[i],那么改变下上述等式就可得arr[i]=diff[i]+arr[i-1],那么就可以从下标1开始遍历diff数组,由于arr[0]和diff[1]的值都有了,那么arr[1]的值也可以求出来,同样地,arr数组后面的元素也能求出来了。
先来道例题。
根据上述思路所写的代码如下所示。
class Solution {
public int[] getModifiedArray(int length, int[][] updates) {
if (length<=0||updates==null){
return new int[0];
}
int[] diff=new int[length];
int[] res=new int[length];
for (int[] update:updates){
int i=update[0];
int j=update[1];
int inc=update[2];
diff[i]+=inc;
if (j+1<length){
diff[j+1]-=inc;
}
}
res[0]=diff[0];
for (int i=1;i<length;i++){
res[i]=diff[i]+res[i-1];
}
return res;
}
}