leetcode-56. Merge Intervals - Medium
descrition
Given a collection of intervals, merge all overlapping intervals.
For example,
Given [1,3],[2,6],[8,10],[15,18],
return [1,6],[8,10],[15,18].
解析
两个思路,都比较有难度。注意算法正确性的证明。
方法 1 - 连通分支
通过图的方法来解决。算法描述如下:
以每个间隔 interval 为图的顶点,如果两个 interval 是包含关系,即有其中一个被另一个包含,则使用无向边将两个顶点连接起来。由此图中每一个连通分量都可以进行合并。问题就转换成了如何求图的联通分量的问题。
此方法并非最优解,详细参考官网的 solution。这里重点介绍方法 2.
方法 2
算法描述:
令 ans 结果返回数组,istart, iend 分别表示当前 interval 的起止。
对于 intervals[] 数组:
- 预处理:根据其起点,即 intervals[i].start 对 intervals 进行非递减有序排序。
- 初始化:istart = intervals[0].start, iend = intervals[0].end
- 循环执行以下步骤( i = [1, ..., n-1]):
- 如果 intervals[i].start <= end:说明当前的 intervals[i] 可以被合并,此时更新 iend = max(iend, intervals[i].end),interval.end 不是有序的,因此要取两段间的最大值最为结尾。
- 否则:说明当前的 intervals[i] 不能被合并,则 (istart, iend) 形成一个新的被加入到 ans 中,并更新 istart = intervals[i].start, iend = intervals[i].end
- 注意最后一次 (istart, iend) 加入到 ans 中。
正确性证明:
使用反证法进行证明。
假设:以上算法不能将某些本应被合并的 interval 进行合并。
根据假设,则说明存在以下情况: 3 个下标,i, j, k,不失一般性令 i < j < k,此时 intervals[i] 和 intervals[k] 可以合并,但 intervals[i] 和 intervals[j],intervals[j] 和 intervals[k] 都不能合并。(算法中只考虑了相邻的 interval,因此当该情况存在时不能被识别)。
由以上假设可以得到不等式:
由不能合并的条件得到:
intervals[i].end < intervals[j].start
intervals[j].end < intervals[k].start
由合并的条件得到:
intervals[i].end >= intervals[k].start
根据 interval 的定义,有 intervals[j].start <= intervals[j].end,由此对以上不等式进行合并得到:
intervals[i].end < intervals[j].start <= intervals[j].end < intervals[k].start
==> intervals[i].end < intervals[k].start
与条件 intervals[i].end >= intervals[k].start 矛盾。
综上,算法得到的结果是正确的。
复杂度分析
- 时间复杂度-O(nlog(n)),取决于排序算法
- 空间复杂度-O(1) 或 O(n),取决于排序算法是否可以原址完成。
code
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Interval{
int start;
int end;
Interval(): start(0), end(0) {}
Interval(int s, int e): start(s), end(e) {}
};
class Solution{
public:
vector<Interval> merge(vector<Interval>& intervals){
vector<Interval> ans;
if(intervals.empty())
return ans;
// sort intervals in ascending by Interval.start
sort(intervals.begin(), intervals.end(), comp);
int istart = intervals[0].start; // the start of the current interval
int iend = intervals[0].end; // the end of the current interval
for(int i=1; i<intervals.size(); i++){
if(intervals[i].start <= iend){
iend = max(iend, intervals[i].end);
}else{
ans.push_back(Interval(istart, iend));
istart = intervals[i].start;
iend = intervals[i].end;
}
}
// don't forget the last interval
ans.push_back(Interval(istart, iend));
return ans;
}
static bool comp(Interval int1, Interval int2){ // in ascending
return int1.start < int2.start;
}
};
int main()
{
return 0;
}