这题可用差分数组来解:力扣1893
什么是差分数组?
假如有个原数组
int[] origin = new int[]{1,2,3,4,5,6,7,8};
那么这个range数组的差分数组就是origin数组相邻元素的差组成的数组。
int n = origin.length;
int[] diff = new int[n];
for(int i=0; i<n; i++){
if(i==0) diff[i] = origin[i];
diff[i] = origin[i]-origin[i-1];
}
差分数组的作用
当原数组中的某段区间内的元素同时增加/减去一个相同的值的时候,可以不必对原数组区间内的元素一个一个更新,只要更新对应差分数组的区间端点即可。
栗子.
- 老方法
//对原数组[2,7]区间的数整体加3
int left = 2,right = 7;
//传统方法,复杂度O(n)
while(left<=right){
origin[left++] += 3;
}
- 新方法
//对原数组[2,7]区间的数整体加3
int left = 2,right = 7;
//引入差分数组后的方法,复杂度O(1)
diff[left] += 3;
diff[right+1] -= 3;
新方法怎么还原修改后的原数组呢?
只要把diff数组求前缀和就可以了。
for(int i=0; i<diff.length; i++){
if(i==0) origin[i] = diff[i];
origin[i] = origin[i-1]+diff[i];
}
为什么求前缀和就能得到原数组的更新后状态呢?
因为我们修改的是整体区间的值,而且修改的大小还是一样的(这两个条件缺一不可)。当这两个条件都满足的情况下,区间内对应的差分数组的值是不变的,我们只要更新区间端点的值即可。
说明:图片来自此处
补充,虽然通过差分数组还原新数组复杂度还是O(n),相比于老方法没有提高,但是,若是修改次数大于1呢?频繁修改呢?我们用新方法可以在m次修改后,以O(m+n)的复杂度获得新数组,老方法的复杂度是O(m*n)。
再补充,由于还原新数组的方法是求动态数组的前缀和,可以想到把差分数组与树状数组结合。将上述复杂度提升为O(M+lgN)。