差分数组是高效处理数组区间修改问题的方法,适用于对原始数组的连续区间频繁地进行增加或减少操作的情形。
- 例如,输入一个数组 n u m s nums nums,要求给区间 n u m s [ 2...6 ] nums[2...6] nums[2...6] 全部加1,再给 $nums[3…9] $ 全部减3,再给 n u m s [ 0...4 ] nums[0...4] nums[0...4] 全部加2,再给……问:最后 n u m s nums nums 数组中各元素的值是什么?
- 【常规思路】:使用 for 循环给给区间 n u m s [ i . . j ] nums[i..j] nums[i..j] 加上 v a l val val,该思路的时间复杂度是 O ( N ) O(N) O(N)。由于这个场景下对 n u m s nums nums 的修改非常频繁,所以效率会很低。
- 【差分数组】:类似前缀和构造的 p r e f i x prefix prefix 数组,对 n u m s nums nums 数组构造 d i f f diff diff 差分数组, d i f f [ i ] = n u m s [ i ] − n u m s [ i − 1 ] diff[i] = nums[i] - nums[i-1] diff[i]=nums[i]−nums[i−1]。
差分数组,记为 d i f f diff diff,是根据原数组 n u m s nums nums 构建的一个新数组,其中 d i f f [ i ] diff[i] diff[i] 表示 n u m s [ i ] nums[i] nums[i] 与 n u m s [ i − 1 ] nums[i - 1] nums[i−1] 的差值。对于数组的第一个元素, d i f f [ 0 ] = n u m s [ 0 ] diff[0] = nums[0] diff[0]=nums[0],因为没有前一个元素与之比较。差分数组的主要优势在于它可以快速、高效地对原始数组的任意连续区间进行修改。
构建差分数组 d i f f diff diff,遵循以下步骤:
int[] diff = new int[nums.length];
diff[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
diff[i] = nums[i] - nums[i - 1];
}

当需要对原数组 n u m s nums nums 的连续区间 [ i . . j ] [i..j] [i..j] 进行修改,即对该区间内所有元素增加或减少某个值 v a l val val 时,我们只需要更新差分数组 d i f f diff diff:
这样,我们就在 O ( 1 ) O(1) O(1) 的时间复杂度内完成了区间的修改,而不需要遍历整个区间。原理很简单,回想 d i f f diff diff 数组反推 n u m s nums nums 数组的过程, d i f f [ i ] + = v a l diff[i] += val diff[i]+=val 意味着给 n u m s [ i . . ] nums[i..] nums[i..] 所有的元素都加了 v a l val val,然后 d i f f [ j + 1 ] − = v a l diff[j+1] -= val diff[j+1]−=val 又意味着对于 n u m s [ j + 1.. ] nums[j+1..] nums[j+1..] 所有元素再减 v a l val val。综合起来,就是对 n u m s [ i . . j ] nums[i..j] nums[i..j] 中的所有元素都加 v a l val val 了

修改完差分数组后,可以通过以下步骤还原修改后的原数组 n u m s nums nums:
int[] res = new int[diff.length];
res[0] = diff[0];
for (int i = 1; i < diff.length; i++) {
res[i] = res[i - 1] + diff[i];
}
本文部分内容参考自:【labuladong】前缀和/差分数组技巧精讲