差分数组

一、差分定义和性质

首先差分主要是针对一个数组中的元素进行频繁的增加或删除。假设现在有一个数组nums,现在我要对里面的某个区间的元素全部加2,

然后要对另外一个区间全部减1。一般的思路是直接for循环进行遍历,这样的话时间复杂度是O(n),而我们使用差分数组的话时间复杂度为O(1)。

对于一个数组nums,它的差分数组diff_array的定义为

if( i == 0 ) diff_array[0] = nums[0];

if( i > 0 ) diff_array[i] = nums[i] - nums[i - 1]

也就是说我们对nums数组中的相邻元素进行两两做差(用右边减去左边),然后再在开头补上一个nums[0]就得到了它的差分数组。

举个例子:

nums = [1,5,6,8,9],那么按照上述操作相减之后的数组就是 diff_array[4,1,2,3],最后我们将nums[0]补在开头,得到最终的差分数组

diff_array[1,4,1,2,1]。这里我们可以用到差分数组的第一个性质:从左到右累加 diff_array 中的元素,可以得到数组 nums(建议自己试一下)

现在我们如果要将nums[1]到nums[3]的元素全部加上5,得到nums1

nums1[1,10,11,13,9]。现在对这个数组进行差分,得到的数组

diff_array1[1,9,1,2,-4]。我们将两个差分数组进行对比,发现只有diff_arrary[1]和diff_arrary[4]改变了,而在上面我们提到了可以通过差分数组还原

为原来的数组,那是不是就说明,我们只需要更改差分数组中的元素,就能够对原数组的一个区间的值进行修改。

通过对比我们发现diff_array[1] += 5得到了diff_arry1[1],而diff_arry[4] -= 5得到了diff_arry1[4]

这里我们得到了第二个性质:对原数组nums的区间 i 到 j 都增加一个数 x 等于 对差分数组diff_array中的diff_array[i] += x,diff_array[j+1] -= x。

那如果是减小x呢?很简单只需要交换一下符号既可。

二、差分数组的运用

1.力扣370(区间加法)

https://leetcode.cn/problems/range-addition/description/

题目如下:

假设你有一个长度为 n 的数组,初始情况下所有的数字均为 0,你将会被给出 k​​​​​​ 个更新的操作。

其中,每个操作会被表示为一个三元组:[startIndex, endIndex, inc],你需要将子数组 A[startIndex ... endIndex](包括 startIndex 和 endIndex)增加 inc。

请你返回 k 次操作后的数组。

样例

输入: length = 5, updates = [[1,3,2],[2,4,3],[0,2,-2]]
输出: [-2,0,3,5,3]

初始状态:[0,0,0,0,0]
进行了操作 [1,3,2] 后的状态:[0,2,2,2,0]

进行了操作 [2,4,3] 后的状态:[0,2,5,5,3]

进行了操作 [0,2,-2] 后的状态:[-2,0,3,5,3]

思路:该题就是直接考察的差分数组,给出了要修改区间的起点,终点,以及要修改的值。

代码实现:

class Solution
{
public:
    std::vector<int> getModifiedArray(int length, std::vector<std::vector<int>> &updates)
    {
        //定义一个ans用于记录结果,diff_array用作差分数组
        std::vector<int>ans(length,0);
        std::vector<int> diff_array(length+10);
        //让diff_array[0] = ans[0]就表示我们将ans[0],补在了差分数组的开头
        diff_array[0] = ans[0];
        //第一步先求一下差分数组,这题其实可以不写,因为初始的数组全是0
        for (int i = 1; i < length;i++)
            diff_array[i] = ans[i] - ans[i - 1];
        
        //第二步进行数组的修改
        for (int i = 0; i < updates.size();i++){
            //updata[i][0]代表修改的起点,updata[i][1] + 1代表要修改的终点,updata[i][2]代表要修改的值
            diff_array[updates[i][0]] += updates[i][2];
            diff_array[updates[i][1] + 1] -= updates[i][2];
        }

        //第三步,通过差分数组还原初始数组
        ans[0] = diff_array[0];
        for (int i = 1; i < length;i++){
            ans[i] += diff_array[i] + ans[i - 1];
        }
        return ans;
    }
};

2.力扣1094(拼车)

题目如下:

车上最初有 capacity 个空座位。车 只能 向一个方向行驶(也就是说,不允许掉头或改变方向)

给定整数 capacity 和一个数组 trips ,  trip[i] = [numPassengersi, fromi, toi] 表示第 i 次旅行有 numPassengersi 乘客,接他们和放他们的位置分别是 fromi 和 toi 。这些位置是从汽车的初始位置向东的公里数。

当且仅当你可以在所有给定的行程中接送所有乘客时,返回 true,否则请返回 false

输入:trips = [[2,1,5],[3,3,7]], capacity = 4 输出:false

输入:trips = [[2,1,5],[3,3,7]], capacity = 5 输出:true

思路:对于该题而言,我们用number[i]表示到i这个地方时车上的人数是多少,如果大于capacity就代表着超载了。题目中的trips[i]就表示我们从

fromi 到 toi 增加了numPassengersi个人。就等于增加区间的值,可以使用差分数组

代码实现:

class Solution
{
public:
    bool carPooling(std::vector<std::vector<int>> &trips, int capacity)
    {
        //第一步创建一个差分数组
        int n = trips.size();
        std::vector<int> v(1010);

        //第二步进行差分
        for (int i = 0; i < n; i++){
            v[trips[i][1]] += trips[i][0];
            v[trips[i][2]] -= trips[i][0];
        }

        //第三步,进行还原,这里还原出来的是到达i时车上的人数。
        int sum = 0;
        for(int number:v){
            sum += number;
            //在还原的时候我们判断sum是否会超过capacity的
            if(sum > capacity)
                return false;
        }
        return true;
    }

};

 

posted @ 2023-12-04 15:55  clinx000  阅读(971)  评论(0编辑  收藏  举报