(十二)算法与数据结构 | 差分数组

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)=Δx0limΔxΔy=Δx0limΔ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)的差分,它反映的是前后两个数据间的变化。现假设有如下数组数据:

索引01234567
025497100

根据差分的概念,求得数组中前后两个元素的差, 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[i1]

索引01234567
025497100
-23-15-23-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[i1]。现给定的应用场景是对数组中某区间内的值同步变化,如区间 [ 2 , 5 ] [2,5] [2,5]内的所有元素值加 3 3 3。原数组值以及差分数组值的变化情况为:

索引01234567
02871210100
-26-15-20-10

由上表可以得到,差分数组中仅有两个位置的值发生变化,即区间左端点处右区间右端点处的下一个位置(如果存在),分别加上和减去相应值。现又另一个变化,区间 [ 3 , 7 ] [3,7] [3,7]内的所有元素减 2 2 2。原数组以及差分数组值的变化情况为:

索引01234567
02851088-2
-26-35-20-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[i1]来还原原数组的值。假设现在有大量针对区间元素的加法和减法操作,借助差分数组可以极大减少算法的时间复杂度,下一节中的例题可以验证。这是差分数组在算法与数据结构中的一种典型应用。

2. LeetCode1109. 航班预订统计

题目来源 1109.航班预订统计

题目描述 这里包含 n 个航班,分别从 1n 进行编号。有一份航班预定表 bookings ,表中第 i 条预定记录 bookings[i] = [firsti, lasti, seatsi] 意味着从 firstilasti (包含二者)的每个航班上预定了 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. 总结

当题目中有对区间元素的频繁操作时,差分数组中一种可能的选择,这道题(地址)也可以使用差分数组解答,思路更加灵活。


参考

  1. https://baike.baidu.com/item/%E5%B7%AE%E5%88%86/10349967 .


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值