关键字:区间,加和
介绍:
前缀和实际上就是求积分(离散),差分就是求导,它们之间互为逆运算。
前缀和主要适用于原数组不变,频繁查询某个区间的累加和。
差分适用于频繁对某区间的元素进行增减(原数组要变),如果是原数组来操作的话,每次区间加减都需要O(N)的时间复杂度,但是差分数组只需要O(1)的时间复杂度,很多次加减操作的话,这个差距就很大了,差分数组回到原数组需要O(N)的时间复杂度,但是只需要一次。
解题范式:
1. 创建diff数组,对每一次加减都对diff进行运算(
for ( auto once : all ){
diff[start] += val;
if(!越界) diff[end] -= val.
}
2. 还原数组,for(int i =1; i< n; i++){ diff[ i ] += diff[ i - 1]; }
练习题
例题1:航班预订统计
分析:
差分和暴力的区别在于,由于差分的性质每次订票是O(1)复杂度,而暴力是O(N),差分将O(N)的操作提到最后去了,只有一次O(N)的操作,所以当订票(增减数组很多的时候),使用差分数组可以得到很高的效率提升。
代码:
class Solution {
public:
vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
vector<int> diff(n);
for(auto booking : bookings){
diff[booking[0] - 1] += booking[2];
if(booking[1] < n){
diff[booking[1]] -= booking[2];
}
}
for(int i = 1; i< n; i++){
diff[i] += diff[i-1];
}
return diff;
}
};
总结:
i)区间加和的模板,差分数组:左区间直接加,右区间不越界需要减去(如果越界说明后面所有都加不需要减了)
ii)差分数组可以直接累加得到原数组,不需要额外开一个空间。
例题2:拼车
分析:这里减法的地方不需要判断是否越界是因为数组开的1001序号能够到1000.
代码:
#define MAXMILE 1001
class Solution {
public:
bool carPooling(vector<vector<int>>& trips, int capacity) {
vector<int> diff(MAXMILE);
for(auto trip : trips){
if(trip[0] > capacity){
return false;
}
diff[trip[1]] += trip[0];
diff[trip[2]] -= trip[0];
}
for(int i = 1; i< MAXMILE; i++){
diff[i] += diff[i-1];
if(diff[i] > capacity){
return false;
}
}
return true;
}
};
总结:无