区间合并总结
区间合并总体来说还是比较简单的,通过模拟基本都能做出来,不过要注意下标越界的情况。
例题:
1.合并区间(https://leetcode-cn.com/problems/merge-intervals/)
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间。
思路:将区间按左端点从小到大排成序列,然后通过对相邻区间的"右左"端点判断是否有交集,然后通过模拟完成区间合并。
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
vector<vector<int>> ans;
sort(intervals.begin(),intervals.end());
int tmp = 0;
for(int i=0;i<intervals.size();i++){
int L = intervals[i][0] , R = intervals[i][1];
if(!ans.size() || ans.back()[1]<L){ //若已放区间中右端点最大值都小于该区间的最小值,则将该区间放入
ans.push_back({L,R});
}
else {
ans.back()[1]=max(ans.back()[1],R); //更新右端点
}
}
return ans;
}
};
2.插入区间(https://leetcode-cn.com/problems/insert-interval/)
给你一个 无重叠的 ,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。
思路:详见代码
class Solution {
public:
vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
vector<vector<int>>ans;
int left = newInterval[0];
int right = newInterval[1];
bool flag=0;
for(auto &interval : intervals){
if(interval[0]>right){ //在插入区间右侧
if(!flag){
ans.push_back({left,right});
flag=1;
}
ans.push_back(interval);
}
else if(interval[1]<left){ //在插入区间左侧
ans.push_back(interval);
}
else {//更新左右端点
left = min(left,interval[0]);
right = max(right,interval[1]);
}
}
if(!flag) ans.push_back({left,right});
return ans;
}
};
3.无重叠区间(https://leetcode-cn.com/problems/non-overlapping-intervals/)
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
思路:详见代码
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
sort(intervals.begin(),intervals.end());
int n = intervals.size();
if(n == 0) return 0;
vector<int>st(n,0); //标记删除区间
int ans = 0;
int L = intervals[0][0],R = intervals[0][1]; //标记被比较的区间
int pos = 0;
for(int i=1;i<n;i++){
if(!st[pos] && !st[i] && intervals[i][0] < R){
if(intervals[i][1] >= R) //删除右端点大的那个区间
st[i] = 1;
else {
st[pos] = 1;
L = intervals[i][0]; //更新被比较的区间
R = intervals[i][1];
pos = i;
}
ans++;
}
else {
L = intervals[i][0];//更新被比较的区间
R = intervals[i][1];
pos = i;
}
}
return ans;
}
};
4.区间列表的交集(https://leetcode-cn.com/problems/interval-list-intersections/)
给定两个由一些 闭区间 组成的列表,firstList 和 secondList ,其中 firstList[i] = [starti, endi] 而 secondList[j] = [startj, endj] 。每个区间列表都是成对 不相交 的,并且 已经排序 。
返回这 两个区间列表的交集 。
形式上,闭区间 [a, b](其中 a <= b)表示实数 x 的集合,而 a <= x <= b 。
两个闭区间的 交集 是一组实数,要么为空集,要么为闭区间。例如,[1, 3] 和 [2, 4] 的交集为 [2, 3] 。
思路:双指针,分别指向两个列表,两个区间不相交时,谁的区间小,移动谁的指针,相交时,则放入列表。注意指针越界。
class Solution {
public:
vector<vector<int>> intervalIntersection(vector<vector<int>>& firstList, vector<vector<int>>& secondList) {
vector<vector<int>> ans;
int l = 0,r = 0;
while(l<firstList.size() && r<secondList.size()){
while(l < firstList.size() && r < secondList.size()){
if(l >= firstList.size() || r >= secondList.size()) break; //防止越界
if(firstList[l][1] < secondList[r][0]) l++; //first区间在second区间左边
else if(firstList[l][0] > secondList[r][1]) r++; //first区间在second区间右边
else break;//说明此时有交集或某一区间已经遍历完
}
if(l >= firstList.size() || r >= secondList.size()) break;//防越界
int lmin = firstList[l][0],lmax = firstList[l][1];
int rmin = secondList[r][0],rmax = secondList[r][1];
// cout<<l<<' '<<r<<endl;
ans.push_back({max(lmin,rmin),min(lmax,rmax)});
//右区间小的++
if(l < firstList.size() && secondList[r][1] > firstList[l][1]) l++;
else if(r < secondList.size() && secondList[r][1] <= firstList[l][1])r++;
}
return ans;
}
};