1. 差分数组
在百度百科中,差分的定义是差分的结果反映了离散量之间的一种变化,是研究离散数学的一种工具,应用场景包括数学、物理学和信息学等。在数学中,我们最熟悉的涉及差分概念的是函数导数的定义,如下:
f
′
(
x
0
)
=
lim
Δ
x
→
0
Δ
y
Δ
x
=
lim
Δ
x
→
0
f
(
x
0
+
Δ
x
)
−
f
(
x
0
)
Δ
x
f'(x_0)=\lim_{\Delta x\rightarrow0}\frac{\Delta y}{\Delta x}=\lim_{\Delta x\rightarrow 0}\frac{f(x_0+\Delta x)-f(x_0)}{\Delta x}
f′(x0)=Δx→0limΔxΔy=Δx→0limΔxf(x0+Δx)−f(x0)
则称 Δ f ( x 0 ) = f ( x 0 + Δ x ) − f ( x 0 ) \Delta f(x_0)=f(x_0+\Delta x)-f(x_0) Δf(x0)=f(x0+Δx)−f(x0)为 f ( x 0 ) f(x_0) f(x0)的差分,它反映的是前后两个数据间的变化。现假设有如下数组数据:
索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
值 | 0 | 2 | 5 | 4 | 9 | 7 | 10 | 0 |
根据差分的概念,求得数组中前后两个元素的差, d i f f [ i ] = a r r [ i ] − a r r [ i − 1 ] diff[i]=arr[i]-arr[i-1] diff[i]=arr[i]−arr[i−1]。
索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
值 | 0 | 2 | 5 | 4 | 9 | 7 | 10 | 0 |
差 | - | 2 | 3 | -1 | 5 | -2 | 3 | -10 |
由上表可知,差分数组的大小比原数组小一,原数组第一个位置对应的差分数组的值视具体情况而定。显然,我们可以根据原数组的第一个以及差分数组的内容还原原数组, a r r [ i ] = d i f f [ i ] + a r r [ i − 1 ] arr[i]=diff[i]+arr[i-1] arr[i]=diff[i]+arr[i−1]。现给定的应用场景是对数组中某区间内的值同步变化,如区间 [ 2 , 5 ] [2,5] [2,5]内的所有元素值加 3 3 3。原数组值以及差分数组值的变化情况为:
索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
值 | 0 | 2 | 8 | 7 | 12 | 10 | 10 | 0 |
差 | - | 2 | 6 | -1 | 5 | -2 | 0 | -10 |
由上表可以得到,差分数组中仅有两个位置的值发生变化,即区间左端点处右区间右端点处的下一个位置(如果存在),分别加上和减去相应值。现又另一个变化,区间 [ 3 , 7 ] [3,7] [3,7]内的所有元素减 2 2 2。原数组以及差分数组值的变化情况为:
索引 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
值 | 0 | 2 | 8 | 5 | 10 | 8 | 8 | -2 |
差 | - | 2 | 6 | -3 | 5 | -2 | 0 | -10 |
差分数组的值与上一次加运算有相似的变化。注意,我们始终可以通过公式 a r r [ i ] = d i f f [ i ] + a r r [ i − 1 ] arr[i]=diff[i]+arr[i-1] arr[i]=diff[i]+arr[i−1]来还原原数组的值。假设现在有大量针对区间元素的加法和减法操作,借助差分数组可以极大减少算法的时间复杂度,下一节中的例题可以验证。这是差分数组在算法与数据结构中的一种典型应用。
2. LeetCode1109. 航班预订统计
题目来源 1109.航班预订统计
题目描述 这里包含 n
个航班,分别从 1
到 n
进行编号。有一份航班预定表 bookings
,表中第 i
条预定记录 bookings[i] = [firsti, lasti, seatsi]
意味着从 firsti
到 lasti
(包含二者)的每个航班上预定了 seatsi
个座位。返回一个长度为 n
的数组 answer
,里面的元素是每个航班预定的座位总数。
例如,输入为 bookings = [[1,2,10],[2,3,20],[2,5,25]], n = 5
,则输出为 [10,55,45,25,25]
。具体解释为:
航班编号 1 2 3 4
预定记录1 10 10
预定记录2 20 20
预定记录3 25 25 25
总预定数 10 55 45 25
暴力法 遍历 bookings
数组,对于数组的每一项,循环操作区间内的元素,时间复杂度为
O
(
m
n
)
O(mn)
O(mn),空间复杂度为
O
(
1
)
O(1)
O(1)。其中,
m
m
m为预定记录数。
vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n)
{
vector<int> answer(n, 0);
for (int i = 0; i < bookings.size(); ++i)
{
int start = bookings[i][0], finish = bookings[i][1], seats = bookings[i][2];
for (int j = start; j <= finish; ++j)
{
answer[j - 1] += seats;
}
}
return answer;
}
差分数组 根据第一节中差分数组的思想,每次仅处理差分数组端点处的值,最后通过差分数组还原原数组。时间复杂度为 O ( m + n ) O(m+n) O(m+n),空间复杂度为 O ( n ) O(n) O(n)。
vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n)
{
// 差分数组
vector<int> diff(n + 1, 0);
// 存放结果
vector<int> answer(n, 0);
for (int i = 0; i < bookings.size(); ++i)
{
int start = bookings[i][0], finish = bookings[i][1], seats = bookings[i][2];
// 打印验证通过
diff[start - 1] += seats;
diff[finish] -= seats;
}
// 遍历差分数组获得结果
ans[0] = diff[0];
for (int i = 1; i < n; ++i)
{
answer[i] = answer[i - 1] + diff[i];
}
return answer;
}
其他题解 官方题解
3. 总结
当题目中有对区间元素的频繁操作时,差分数组中一种可能的选择,这道题(地址)也可以使用差分数组解答,思路更加灵活。
参考
- https://baike.baidu.com/item/%E5%B7%AE%E5%88%86/10349967 .