差分数组
- leetcode 1094 拼车
- 差分数组
- 代码演示:
- 前缀和数组
leetcode 1094 拼车
难度 - 中等
原题链接 - 拼车
车上最初有 capacity 个空座位。车 只能 向一个方向行驶(也就是说,不允许掉头或改变方向)
给定整数 capacity 和一个数组 trips , trip[i] = [numPassengersi, fromi, toi] 表示第 i 次旅行有 numPassengersi 乘客,接他们和放他们的位置分别是 fromi 和 toi 。这些位置是从汽车的初始位置向东的公里数。
当且仅当你可以在所有给定的行程中接送所有乘客时,返回 true,否则请返回 false。
示例 1:
输入:trips = [[2,1,5],[3,3,7]], capacity = 4
输出:false
示例 2:
输入:trips = [[2,1,5],[3,3,7]], capacity = 5
输出:true
提示:
1 <= trips.length <= 1000
trips[i].length == 3
1 <= numPassengersi <= 100
0 <= fromi < toi <= 1000
1 <= capacity <= 1e5
差分数组
差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减。
比如说,我给你输入一个数组 nums,然后又要求给区间 nums[2…6] 全部加 1,再给 nums[3…9] 全部减 3,再给 nums[0…4] 全部加 2,再给…
常规的思路很容易,你让我给区间 nums[i…j] 加上 val,那我就一个 for 循环给它们都加上呗,还能咋样?这种思路的时间复杂度是 O(N),由于这个场景下对 nums 的修改非常频繁,所以效率会很低下。
这里就需要差分数组的技巧,类似前缀和技巧构造的 preSum 数组,我们先对 nums 数组构造一个 diff 差分数组,diff[i] 就是 nums[i] 和 nums[i-1] 之差:
int[] diff = new int[nums.length];
// 构造差分数组
diff[0] = nums[0];
for (int i = 1; i < nums.length; i++) {
diff[i] = nums[i] - nums[i - 1];
}
这样构造差分数组 diff,就可以快速进行区间增减的操作,如果你想对区间 nums[i…j] 的元素全部加 3,那么只需要让 diff[i] += 3,然后再让 diff[j+1] -= 3 即可:
原理很简单,回想 diff 数组反推 nums 数组的过程,diff[i] += 3 意味着给 nums[i…] 所有的元素都加了 3,然后 diff[j+1] -= 3 又意味着对于 nums[j+1…] 所有元素再减 3,那综合起来,是不是就是对 nums[i…j] 中的所有元素都加 3 了?
只要花费 O(1) 的时间修改 diff 数组,就相当于给 nums 的整个区间做了修改。多次修改 diff,然后通过 diff 数组反推,即可得到 nums 修改后的结果。
代码演示:
public boolean carPooling1(int[][] trips, int capacity) {
int[] diff = new int[1001];
// 填充差分数组
for (int i = 0; i < trips.length; i++) {
diff[trips[i][1]] += trips[i][0];
// 这里不是diff[trips[i][2]+1]计算的原因:[from,to)是左闭右开区间,to位置时乘客已经下车,不占用车的容量;
diff[trips[i][2]] -= trips[i][0];
}
// 计算每个位置上的乘客数量,如果超过则直接返回false
int currCapacity = 0;
for (int i = 0; i < diff.length; i++) {
currCapacity += diff[i];
if (currCapacity > capacity) {
return false;
}
}
return true;
}
前缀和数组
leetcode303. 区域和检索 - 数组不可变
leetcode304. 二维区域和检索 - 矩阵不可变