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]
.
解法一:从头开始进行两两合并
/**
* Definition for an interval.
* public class Interval {
* int start;
* int end;
* Interval() { start = 0; end = 0; }
* Interval(int s, int e) { start = s; end = e; }
* }
*/
class Interval implements Comparable<Interval>
{
int start;
int end;
Interval() { start = 0; end = 0; }
Interval(int s, int e) { start = s; end = e; }
public int compareTo(Interval o)
{
return this.start - o.start;
}
}
public class Solution {
public static List<Interval> merge(List<Interval> intervals)
{
Collections.sort(intervals);
List<Interval> result = new ArrayList<Interval>();
int length = intervals.size();
if(length <= 1)
return intervals;
Interval first = intervals.get(0),second = new Interval();
for(int i = 1; i < length; i++)
{
second = intervals.get(i);
if(second.start > first.end)
{
result.add(first);
first = second;
}
else
{
first.end = Math.max(first.end, second.end);
}
}
//最后一次合并
result.add(first);
return result;
}
}
解法二:
区间合并 和 求最长区间 一样,关键在于如何记录区间信息,能够降低复杂度。
最简单的思路是用一个bool数组,记录区间中的每一位是否被覆盖。
例如:
intervals: [1, 4] [8, 9] [20, 30] [7, 14] [2, 9]
设一个数组 bool f[30],初始化全部为false
[1, 4]就把f[1]..f[4]全部置为true
其他区间一次类推,最后扫描f数组,就能得到区间合并的结果。
这种做法对于小数据还行,大数据会超市。
这样做,存在很多重复工作,例如[1, 4] [8, 9] [7, 14] [2, 9]这四个区间,会反复将f[2] f[3] f[4][ f[5] f[6] f[7] f[8] f[9]设为true。
所以,我们需要考虑的是如何去除重复的工作。实际上,两个端点就能描述出一个区间,关键在于如何标记出两个端点,让我们在扫描时能够知道这个区间被覆盖。
一个很巧妙的做法就是进入一个区间就加1,出一个区间就减1,如果数值大于0,说明当前处于一个区间中。
还是以刚刚的intervals为例。
设数组 int l[30], r[30],初始化全部为0
[1, 4]:l[1]++, r[4]--
[8, 9]:l[8]++, r[9]--
...
扫描时,用一个count记录,每次:
count += l[i],如果count>0说明i处于一个区间中
count -= r[i],如果count==0说明i是一个区间的尾端点
public static List<Interval> merge(List<Interval> intervals)
{
List<Interval> result = new ArrayList<Interval>();
int length = intervals.size();
if(length <= 1)
return intervals;
int max = Integer.MIN_VALUE, min = Integer.MAX_VALUE;
for(int i = 0;i < length; i++)
{
Interval tmp = intervals.get(i);
max = Math.max(max, tmp.end);
min = Math.min(min, tmp.start);
}
int[] left = new int[max + 1];
int[] right = new int[max + 1];
for(int i = 0;i < length; i++)
{
Interval tmp = intervals.get(i);
left[tmp.start]++;
right[tmp.end]--;
}
int start = -1, count = 0;
for(int i = min; i <= max; i++)
{
count += left[i];
if(count > 0 && start == -1)
start = i;
count += right[i];
if(count == 0 && start != -1)
{
result.add(new Interval(start, i));
start = -1;
}
}
return result;
}