【LeetCode-贪心】合并区间
题目描述
给出一个区间的集合,请合并所有重叠的区间。
示例:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
输入: [[1,4],[4,5]]
输出: [[1,5]]
解释: 区间 [1,4] 和 [4,5] 可被视为重叠区间。
题目链接: https://leetcode-cn.com/problems/merge-intervals/
思路
两个区间[a,b]和[c,d]重合的条件是b>=c。首先将所有区间按照区间的左端点升序排序,然后遍历区间,判断当前区间的右端点和下一个区间的左端点之间的大小关系,根据大小关系决定是否合并。代码如下:
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if(intervals.empty()) return {};
vector<vector<int>> ans;
sort(intervals.begin(), intervals.end());
int i = 0;
int j = 1;
while(i<intervals.size()){
j = i;
int rightMax = intervals[i][1]; // 当前已合并区间的右端点
while(j<intervals.size() && (intervals[i][1]>=intervals[j][0] || rightMax>=intervals[j][0])){ // 注意这个判断条件
rightMax = max(rightMax, intervals[j][1]);
j++;
}
ans.push_back({intervals[i][0], rightMax});
i = j;
}
return ans;
}
};
在判断条件中加了rightMax>=intervals[j][0]
是为了防止[[0,2],[1,4],[3,5]]
这种情况下出错。
- 时间复杂度:O(nlogn+n)=O(nlogn)
O(nlogn)为排序时间,O(n)为区间合并时间,其中n为区间数量。 - 空间复杂度: O(logn)
O(logn)为排序所需空间复杂度。
更简洁的写法
思路是一样的,但上面的代码写复杂了,而且判断的条件容易出错,这个写法来自于官方题解。使用ans来存储合并后的区间,对区间进行排序后首先将第一个区间加入到ans中,然后比较ans的最后一个区间的右端点b和下一个区间的左端点c:如果b>=c,则更新b;否则将下一个区间加入到ans中。代码如下:
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if(intervals.empty()) return {};
vector<vector<int>> ans;
sort(intervals.begin(), intervals.end()); // 别忘了先排序
ans.push_back({intervals[0][0], intervals[0][1]});
for(int i=1; i<intervals.size(); i++){
int curLen = ans.size()-1;
if(ans[curLen][1]<intervals[i][0]){
ans.push_back({intervals[i][0], intervals[i][1]});
}else{
ans[curLen][1] = max(intervals[i][1], ans[curLen][1]);
}
}
return ans;
}
};
- 时间复杂度:O(nlogn+n)=O(nlogn)
O(nlogn)为排序时间,O(n)为区间合并时间,其中n为区间数量。 - 空间复杂度: O(logn)
O(logn)为排序所需空间复杂度。
还有一种写法
感觉更好理解:
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if (intervals.size() < 2) return intervals;
sort(intervals.begin(), intervals.end()); // 先排序
vector<vector<int>> ans;
int left = intervals[0][0]; // 当前区间[left, right]
int right = intervals[0][1];
for (int i = 1; i < intervals.size(); i++) {
if (right < intervals[i][0]) { // 当前区间和下一个区间不相交
ans.push_back({left, right});
left = intervals[i][0];
right = intervals[i][1];
} else if (right >= intervals[i][0]) { // 当前区间和下一个区间相交
if (right <= intervals[i][1]) {
right = intervals[i][1]; // 当前区间不包含下一个区间才更新right
}
}
}
ans.push_back({left, right});
return ans;
}
};