差分数组
一、差分定义和性质
首先差分主要是针对一个数组中的元素进行频繁的增加或删除。假设现在有一个数组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; } };