一维差分(扫描线)基础篇
参考资料来源灵神在力扣所发的题单,仅供分享学习笔记和记录,无商业用途。
公式:d[i]=a[0],a[i]−a[i−1],i≥1
核心思路:
从左到右累加 d 中的元素,可以还原数组a。
对数组进行区间数据操作可以转化成:对差分数组d[i]正向操作,d[j+1]反向操作
应用场景:差分仅适用于区间修改、最终查询或多次区间修改后单次查询的场景
模板题:2848. 与车相交的点
class Solution {
public:int numberOfPoints(vector<vector<int>>& nums) {//题意:给定一组起点~终点数据,求所有路线不重复之和//思路:根据差分思想给起点进行标记+1,终点+1位置进行标记-1,在根据当前前缀和>0说明当前下标在路线上,反之亦然int buff=0,ret=0;for(auto x:nums) buff=max(buff,x[1]);vector<int> ans(buff+2);for(auto x:nums){ans[x[0]]++;ans[x[1]+1]--;}buff=0;for(auto x:ans){buff+=x;buff>0?ret++:0;}return ret;}
};
1893. 检查是否区域内所有整数都被覆盖
题意:给定一组区间数据和单个区间,求这组数据能否全覆盖单个区间
思路:采用差分思想对有交集部分进行标记,然后对该目标区间进行前缀和操作,期间有一次<=0说明没完全覆盖
class Solution {
public:bool isCovered(vector<vector<int>>& ranges, int left, int right) {//题意:给定一组区间数据和单个区间,求这组数据能否全覆盖单个区间//思路:采用差分思想对有交集部分进行标记,然后对该目标区间进行前缀和操作,期间有一次<=0说明没完全覆盖vector<int> ans(right+2);for(auto x:ranges){int s=max(x[0],left); int e=min(x[1],right);if(s>e) continue; //剔除掉不相交区间ans[s]++; //有交集区间:如果起点<left,直接对left进行+1标记ans[e+1]--; //有交集区间,如果终点>right,直接对right+1进行-1标记}int buff=0;for(int i=left; i<=right;i++){buff+=ans[i];if(buff<=0) return false;}return true;}
};
1854. 人口最多的年份
题意:给定一组人的出生和死亡年份,求人口最多且最早的年份。人不应当计入他们死亡当年的人口中。
思路:根据差分思想进行标记,对差分数组进行遍历前缀和操作,前缀和是多少就代表当前年份有多少人存在,即可求解
class Solution {
public:int maximumPopulation(vector<vector<int>>& logs) {//题意:给定一组人的出生和死亡年份,求人口最多且最早的年份。人不应当计入他们死亡当年的人口中。//思路:根据差分思想进行标记,对差分数组进行遍历前缀和操作,前缀和是多少就代表当前年份有多少人存在,即可求解vector<int> ans(2051);for(auto x:logs){ans[x[0]]++;ans[x[1]]--;}int buff=0,ret=0,year;for(int i=1950;i<ans.size();i++){buff+=ans[i];if(buff>ret){ret=buff;year=i;}}return year;}
};
2960. 统计已测试设备
题意:给定一组数据,当前数据大于0则计数+1,对后续所有数据进行-1操作
思路:
差分思想,需要对区间频繁进行数据操作,可缩减成对差分数组d[i]正向操作,d[j+1]反向操作
由于题目不需要获取更新后的数据可无需创建差分数组,当满足条件的时候计数并更新后续需要减的数即可
class Solution {
public:int countTestedDevices(vector<int>& batteryPercentages) {//题意:给定一组数据,当前数据大于0则计数+1,对后续所有数据进行-1操作//思路:差分思想,需要对区间频繁进行数据操作,可缩减成对差分数组d[i]正向操作,d[j+1]反向操作//由于题目不需要获取更新后的数据可无需创建差分数组,当满足条件的时候计数并更新后续需要减的数即可int ret=0,buff=0;for(auto x:batteryPercentages){x+=buff;if(x>0){ret++;buff--;}}return ret;}
};
1094. 拼车
题意:
给定一组[乘客,起点,终点]的数据,公交车只有capacity个位置,能否完成这组数据(终点算空座)
思路:
根据差分定理完成标记,然后按照地址顺序遍历计算前缀和判断是否人数大于座位数
class Solution {
public:bool carPooling(vector<vector<int>>& trips, int capacity) {//题意:给定一组[乘客,起点,终点]的数据,公交车只有capacity个位置,能否完成这组数据(终点算空座)//思路:根据差分定理完成标记,然后按照地址顺序遍历计算前缀和判断是否人数大于座位数map<int,int> m;for(auto x:trips){m[x[1]]+=x[0];m[x[2]]-=x[0];}int buff=0;for(auto [_,x]:m){buff+=x;if(buff>capacity) return false;}return true;}
};
1109. 航班预订统计
题意:
给定一组[航班a,航班b,座位数x]的数据,也就是a~b之间所有航班都订了x个座位数,求每个航班需要多少座位数
思路:
因为需要对区间数据频繁修改,所以采用差分思想,默认航班需要座位数都为0,那么差分数组也都为0。
对给定的数据进行标记,然后遍历差分数组进行前缀和计算求出对应航班需要的座位数
class Solution {
public:vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {//题意:给定一组[航班a,航班b,座位数x]的数据,也就是a~b之间所有航班都订了x个座位数,求每个航班需要多少座位数//思路:因为需要对区间数据频繁修改,所以采用差分思想,默认航班需要座位数都为0,那么差分数组也都为0。//对给定的数据进行标记,然后遍历差分数组进行前缀和计算求出对应航班需要的座位数vector<int> ans(n+2);vector<int> ret(n);for(auto x:bookings){ans[x[0]]+=x[2];ans[x[1]+1]-=x[2];}int buff=0;for(int i=1;i<=n;i++){buff+=ans[i];ret[i-1]=buff;}return ret;}
};
3355. 零数组变换 I
题意:
给定一组数据和一组操作数组(对区间内元素各-1),要求经过操作后新数据的元素是否都<=0
思路:
考虑到对区间频繁进行数据修改,采用差分思想。初始化差分数组都为0,遍历操作数组进行标记
遍历差分数组计算前缀和并与原下标元素相加判断是否<=0
class Solution {
public:bool isZeroArray(vector<int>& nums, vector<vector<int>>& queries) {//题意:给定一组数据和一组操作数组(对区间内元素各-1),要求经过操作后新数据的元素是否都<=0//思路:考虑到对区间频繁进行数据修改,采用差分思想。初始化差分数组都为0,遍历操作数组进行标记//遍历差分数组计算前缀和并与原下标元素相加判断是否<=0vector<int> ans(nums.size()+1);for(auto x:queries){ans[x[0]]--;ans[x[1]+1]++;}int buff=0;for(int i=0;i<nums.size();i++){buff+=ans[i];if(nums[i]+buff>0) return false;}return true;}
};