一、概念
差分是一种处理区间修改和单点查询的高效技巧。它通过构建一个辅助数组(差分数组),将原数组的区间操作转化为对差分数组的单点操作,从而将时间复杂度从 O (n) 降至 O (1)。
二、差分的核心原理
差分通过构建一个辅助数组(差分数组)来简化原数组的区间更新。
首先进行差分数组的定义
给定原数组 a[1...n],其差分数组 d[1...n] 定义为:
d[1] = a[1]
d[2] = a[2] - a[1]
...
d[i] = a[i] - a[i-1](其中 i > 1)
原数组与差分数组的关系:差分数组的前缀和等于原数组。
d[1] = a[1]
d[2] = a[2] - a[1]
d[3] = a[3] - a[2]
d[4] = a[4] - a[3]
...
也就是说,对任意 i,a[i] = d[1] + d[2] + ... + d[i]。
这是差分能够反向推导原数组的关键。
三、差分的核心应用:区间更新
差分的最大价值在于高效处理"对原数组某一区间 [l, r] 内所有元素加 / 减一个值 k" 的操作。
区间更新的转化规则无需遍历原数组的 [l, r] 区间,只需对差分数组 d 做两处单点修改:
d[l] = d[l]+ k:表示从 l 位置开始,原数组所有元素增加 k。
d[r+1]= a[r+1]- a[r]
由于此时a[r]已经增加了k,而 a[r+1]没有变化,因此新的d[r+1]减少了k
即新的d[r+1]=旧的d[r+1]- k(若 r+1 <= n):表示从 r+1 位置开始,抵消之前的增加操作,确保仅 [l, r] 区间受影响。
操作流程示例
假设原数组 a = [1, 2, 3, 4, 5],需对 [2,4] 区间各元素加 2:
初始差分数组 d = [1, 1, 1, 1, 1]
执行更新:d[2] += 2 → d[2] = 3;d[5] -= 2 → d[5] = -1
更新后差分数组 d = [1, 3, 1, 1, -1]
求前缀和得到新原数组:a = [1, 4, 5, 6, 5],符合预期。
四、差分的适用场景
差分并非万能,它仅适用于特定类型的问题,主要包括:
多组区间更新 + 一次最终查询:例如多次给不同区间加值,最后输出完整数组。
线性结构数据:仅适用于一维数组或可转化为一维数组的场景(如二维差分需特殊处理)。
五、代码实现
题目:给定数组 a,多次执行:对 [l, r] 内所有数 +val,最后输出整个数组。
c
#include <stdio.h>
#define N 100
int main() {
int a[N] = {0}; // 原数组
int d[N] = {0}; // 差分数组
int n, m;
int i;
// 1. 输入数组长度 n
printf("请输入元素个数 n:");
scanf("%d", &n);
printf("请输入 %d 个整数:", n);
for (i = 1; i <= n; i++) {
scanf("%d", &a[i]);
}
// 2. 初始化差分数组
d[1] = a[1];
for (i = 2; i <= n; i++) {
d[i] = a[i] - a[i - 1];
}
// 3. 操作次数 m
printf("请输入操作次数 m:");
scanf("%d", &m);
// 4. 执行 m 次区间加操作
while (m--) {
int l, r, val;
printf("输入 l r val:");
scanf("%d %d %d", &l, &r, &val);
d[l] += val;
if (r + 1 <= n)
d[r + 1] -= val;
}
// 5. 求前缀和,还原数组
a[1] = d[1];
for (i = 2; i <= n; i++) {
a[i] = a[i - 1] + d[i];
}
// 6. 输出结果
printf("最终数组:");
for (i = 1; i <= n; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
运行程序
输入:
请输入元素个数 n:5
请输入 5 个整数:1 2 3 4 5
请输入操作次数 m:2
输入 l r val:2 4 10
输入 l r val:1 3 5
过程:
原数组:1 2 3 4 5
操作 1:[2,4] +10 → 1 12 13 14 5
操作 2:[1,3] +5 → 6 17 18 14 5
输出结果:
最终数组:6 17 18 14 5