从【拼车】问题的解法,简单介绍差分数组

本文介绍了在解决车票问题中如何利用差分数组来高效计算乘客总数,避免遍历整个区间。差分数组通过记录每次上车和下车的乘客变化,仅需针对起始和结束位置进行操作,显著减少了计算复杂性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 引入:

        力扣上第1094题,【拼车】

        当时解这道题初步的想法是定义一个数组,下标表示里程数即车当前的位置,元素表示在这个时间点上,车内的乘客总人数

因为车的位置是[0~1000]        所以,定义的数组的下标范围也就是0到1000

例如示例1:

        在第1公里的时候,上两个人,在第5公里的时候下车,所以对数组下标为[1~4]的区间内的元素每一个都加上2

        在第3公里的时候,上三个人,在第7公里的时候下车,所以对数组下标为[3~6]的区间内的元素每一个都加上3

对这个数组的操作就是

这样一个数组的结果就是:

0     2    2    5    5    3    3    0    0    0........0

可以看到这个数组的最大值是5,也就表示这辆车上最多的时候有5个人

只要判断这样一个数组的最大值,有没有超过车上座位的数量就可以了

        但是这就带来了一个问题:区间越大,需要操作的次数就越多

        如果一个人从第1公里坐到了第999公里,另外一个人从第2公里坐到了第1000公里,需要对数组中这两个区间的元素操作的次数就会大大增加。夸张点讲,试想如果这辆车行驶的总里程数是一万公里,或者十万公里呢?

        这个时候,传统的遍历修改方法显然是不太适合的,那么有没有什么方法,能够不用进行这么多次的操作,就侧面反映出数组某个区间的整体变化情况呢?

        此时就可以使用差分数组

差分数组定义

        第一项与原数组相同,之后的每一项都等于原数组的这一项与前一项之差

例如: 

原数组为 5 4 7 2 4 3 1

对应的差分数组为 5  (4-5)  (7-4)  (2-7)  (4-2)  (3-4)  (1-3)

                         即 5     -1      3        -5       2        -1      -2

性质一:

差分数组有一个重要的性质:对差分数组求前缀和数组,等于原数组

什么是前缀和数组?

 前缀和的定义很简单,就是数组这个位置之前的所有元素之和

还是拿上面的差分数组举例:

原数组:5 4 7 2 4 3 1

差分数组为:5     -1                   3                       -5       2        -1      -2

求前缀和为:5      4 (5+(-1))      7 (5+(-1)+3)       2        4        3        1

不难看出,前缀和数组就是原数组,求差分和求前缀这两个操作其实可以看成互逆操作。

那么差分数组是如何从侧面反映出原数组的区间整体变化情况的呢?

性质二:

        如果对原数组的某一个区间进行整体的增减,那么对应的差分数组只有区间首,以及区间尾的下一个位置会发生改变。

        例如:要对上面的数组的[1~4]进行整体+2
原数组就变为:5 6 9 4 6 3 1

           原差分数组:5     -1      3      -5      2       -1    -2
差分数组此时变为:5   (6-5) (9-6) (4-9) (6-4) (3-6) (1-3)
                         即:5      1      3       -5      2       -3    -2

                                        +2                                -2

此时发现,只有差分数组的[1] [5]发生了改变,[1]加上了改变量,[5]减去了改变量。

        由此性质可以反推,当差分数组的[ i ]+a,[ j+1 ]-a,此时求其前缀和数组,还原出来的原数组,在[ i~j ] 的区间上整体加上了a。

        当[i~j]这个区间非常大时,如果对原数组一个一个操作,将非常耗时。而用差分数组的性质反操作原数组,只需要进行两步操作,大大节约了时间,将区间操作转化为了节点操作。

 具体代码:

bool carPooling(int** trips, int tripsSize, int* tripsColSize, int capacity) {
    *tripsColSize = 3;
    int tomax = trips[0][2];    //找到最后一个下车的位置,申请空间的时候就不需要从0~1000
    for(int i=1;i<tripsSize;i++)
    {
       if(tomax<trips[i][2])
       tomax = trips[i][2];
    }

    int* diff = (int*)malloc(sizeof(int)*(tomax+1));
    memset(diff,0,sizeof(int)*(tomax+1));  //数组初始化

    for(int i=0;i<tripsSize;i++)
    {
        diff[trips[i][1]] += trips[i][0];  //上车位置加上乘客改变量
        diff[trips[i][2]] -= trips[i][0];  //下车位置减去乘客改变量
        if(diff[trips[i][1]]>capacity)     //假如加完以后发现已经大于最大座位数
        return false;                   //直接返回false
    }

    int count = 0;
    for(int i=0;i<tomax;i++)
    {
        count += diff[i];        //还原原数组,直接找到最大值
        if(count>capacity)
        return false;
    }
    free(diff);
    return true;
}

 总结:

        差分数组主要的适用场景是对原始数组进行频繁的区间增减操作,这个时候使用差分数组能够快速的完成,同时能够快速获得更新后的数组各个位置的值。

        主要就是把区间遍历操作,变为了对两个节点进行操作,再根据性质转换成原数组。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值