给你一个 无重叠的 ,按照区间起始端点排序的区间列表 intervals
,其中 intervals[i] = [starti, endi]
表示第 i
个区间的开始和结束,并且 intervals
按照 starti
升序排列。同样给定一个区间 newInterval = [start, end]
表示另一个区间的开始和结束。
在 intervals
中插入区间 newInterval
,使得 intervals
依然按照 starti
升序排列,且区间之间不重叠(如果有必要的话,可以合并区间)。
返回插入之后的 intervals
。
注意 你不需要原地修改 intervals
。你可以创建一个新数组然后返回它。
示例 1:
输入:intervals = [[1,3],[6,9]], newInterval = [2,5] 输出:[[1,5],[6,9]]
示例 2:
输入:intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8] 输出:[[1,2],[3,10],[12,16]] 解释:这是因为新的区间[4,8]与[3,5],[6,7],[8,10]重叠。
提示:
0 <= intervals.length <=
intervals[i].length == 2
0 <= starti <= endi <=
intervals
根据starti
按 升序 排列newInterval.length == 2
0 <= start <= end <=
剖析题意:
- 区间列表无重叠。
- 区间列表按照起始端点排序。
基于以上两点,我们可以推出,区间列表的结束端点也是有序的。因为对两个区间列表[i,j]、[m,n]一定有i < j < m < n,即j < n。
对于需要插入的区间[a,b]:
- 一定能找到一个[i,j]满足j是小于a的最大元素,这里就是插入区间的起始位置。
- 还一定能找到一个[m,n]满足m是大于b的最小元素,这里也就是插入区间的结束位置。
- 而处于[i,j]到[m,n]之间的区间,就是和插入区间有重叠的区间,需要删除。
那么现在的问题就转变成了找到[i,j]和[m,n]。
利用c++提供的上下界函数即可获得指向[i,j]和[m,n]的迭代器:
auto left = lower_bound(intervals.begin(),intervals.end(),newInterval[0],[](auto& a, int b) { return a[1] < b; });
auto right = upper_bound(left,intervals.end(),newInterval[1],[](int b,auto& a) { return a[0] > b; });
这里遗留了一个问题:为什么交换匿名函数的参数顺序就会出错? 例如这样:
auto right = upper_bound(left,intervals.end(),newInterval[1],[](auto& a,int b) { return a[0] > b; });
他们的比较函数不都是:
bool compare(const Type1 &a, const Type2 &b);
而且搜到的资料都说std::upper_bound
算法期望的比较函数的参数顺序与std::lower_bound
相同。比较函数应该接受两个参数:
- 第一个参数是容器中的元素类型。
- 第二个参数是你要查找的值或键的类型。
所以是什么导致了这样的错误?
得到right和left之后,判断是否需要合并:
if(left != intervals.end()){
newInterval[0] = min(newInterval[0],left[0][0]);
}
if(right != intervals.begin()){
newInterval[1] = max(newInterval[1],right[-1][1]);
}
如果left区间的左端点更小,说明插入区间和left产生了交叉,所以起始端点更新为left[0][0]。right同理。
在插入前删除left迭代器和right迭代器中间的区间,即删除重叠区间。
intervals.insert(intervals.erase(left,right),newInterval);
-
intervals.erase(left, right)
:从intervals
中删除从left
到right
之间的元素。left
和right
是迭代器,分别指向要删除的第一个元素和要删除的最后一个元素之后的一个元素。因此,此操作将删除与newInterval
重叠的所有现有区间。 -
intervals.insert(intervals.erase(left, right), newInterval)
:在从left
到right
删除的元素处插入newInterval
。由于intervals.erase(left, right)
已经删除了与newInterval
重叠的区间,因此newInterval
将被插入到正确的位置,使得它与任何剩余的区间都不重叠。
完整代码:
class Solution {
public:
vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
auto left = lower_bound(intervals.begin(),intervals.end(),newInterval[0],[](auto& a, int b) { return a[1] < b; });
auto right = upper_bound(left, intervals.end(), newInterval[1], [](const std::vector<int>& a, int b) { return a[0] > b; });
if(left != intervals.end()){
newInterval[0] = min(newInterval[0],left[0][0]);
}
if(right != intervals.begin()){
newInterval[1] = max(newInterval[1],right[-1][1]);
}
intervals.insert(intervals.erase(left,right),newInterval);
return intervals;
}
};