举例
考虑数组
a
=
[
1
,
3
,
3
,
5
,
8
]
a=[1,3,3,5,8]
a=[1,3,3,5,8],对其中的相邻元素两两作差(右边减左边),得到数组
[
2
,
0
,
2
,
3
]
[2,0,2,3]
[2,0,2,3]。然后在开头补上
a
[
0
]
a[0]
a[0],得到差分数组
d
=
[
1
,
2
,
0
,
2
,
3
]
d=[1,2,0,2,3]
d=[1,2,0,2,3]
这有什么用呢?如果从左到右累加
d
d
d 中的元素,我们就「还原」回了
a
a
a 数组
[
1
,
3
,
3
,
5
,
8
]
[1,3,3,5,8]
[1,3,3,5,8]。这类似求导与积分的概念。
这又有什么用呢?现在把连续子数组
a
[
1
]
,
a
[
2
]
,
a
[
3
]
a[1],a[2],a[3]
a[1],a[2],a[3] 都加上
10
10
10,得到
a
′
=
[
1
,
13
,
13
,
15
,
8
]
a^{\prime}=[1,13,13,15,8]
a′=[1,13,13,15,8]。再次两两作差,并在开头补上
a
′
[
0
]
a^′[0]
a′[0],得到差分数组
d
′
=
[
1
,
12
,
0
,
2
,
−
7
]
d ^′=[1,12,0,2,−7]
d′=[1,12,0,2,−7]
对比
d
d
d 和
d
′
d^′
d′,可以发现只有
d
[
1
]
d[1]
d[1] 和
d
[
4
]
d[4]
d[4] 变化了,这意味着对
a
a
a 中连续子数组的操作,可以转变成对差分数组
d
d
d 中两个数的操作。
定义和性质
对于数组 a a a,定义其差分数组(difference array)为
d [ i ] = { a [ 0 ] , i = 0 a [ i ] − a [ i − 1 ] , i ≥ 1 d[i]=\left\{\begin{array}{ll} a[0], & i=0 \\ a[i]-a[i-1], & i \geq 1 \end{array}\right. d[i]={a[0],a[i]−a[i−1],i=0i≥1
性质 1:从左到右累加 d d d 中的元素,可以得到数组 a a a。
性质 2:如下两个操作是等价的。
- 把 a a a 的子数组 a [ i ] , a [ i + 1 ] , ⋯ , a [ j ] a[i],a[i+1],⋯,a[j] a[i],a[i+1],⋯,a[j] 都加上 x x x。
- 把 d [ i ] d[i] d[i] 增加 x x x,把 d [ j + 1 ] d[j+1] d[j+1] 减少 x x x。
利用性质 2,我们只需要 O ( 1 ) O(1) O(1) 的时间就可以完成对 a a a 的子数组的操作。最后利用性质 1 从差分数组复原出数组 a a a。
注:也可以这样理解, d [ i ] d[i] d[i] 表示把下标 ≥ i ≥i ≥i 的数都加上 d [ i ] d[i] d[i]。
示例
参考资料:
https://leetcode.cn/problems/car-pooling/solutions/2550264/suan-fa-xiao-ke-tang-chai-fen-shu-zu-fu-9d4ra/