题目地址:
https://www.lintcode.com/problem/1280/
要求设计一个数据结构,可以实现下面操作:
1、添加一个数;
2、得到已经添加的数形成的闭区间族,从小到大返回,要求连续的数要合并为一个区间,相邻区间至少中间要隔开一个数,并且区间两两不相交。
思路是利用TreeSet。这个TreeSet直接用来存区间。当添加一个数 x x x的时候,先去查一下右端点最大且小于等于 x x x的那个区间(即floor),和左端点最小且大于等于 x x x的那个区间(即ceiling),如果这两个区间都不能和 x x x合并,那就直接将单点区间 [ x , x ] [x,x] [x,x]加入进TreeSet;否则先将 x x x与俩区间进行合并,如果floor和ceiling得到的俩区间还能进一步合并,则删掉floor那个区间,然后更新ceiling的右端点(至于是删除floor更新ceiling,还是删除ceiling更新floor,这要看TreeSet的Comparator如何定义。如果TreeSet里的区间按照左端点排序的,那么就要删floor)。为了避免floor和ceiling得到null的麻烦,可以预先将正负无穷两个数的单点区间加入TreeSet。代码如下:
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
public class Solution {
private TreeSet<Interval> set = new TreeSet<>((i1, i2) -> Integer.compare(i1.start, i2.start));
{
set.add(new Interval(Integer.MIN_VALUE, Integer.MIN_VALUE));
set.add(new Interval(Integer.MAX_VALUE, Integer.MAX_VALUE));
}
/**
* @param val: An integer.
* @return: nothing
*/
public void addNum(int val) {
// write your code here
Interval interval = new Interval(val, val);
Interval floor = set.floor(interval), ceiling = set.ceiling(interval);
if (floor.end < val - 1 && ceiling.start > val + 1) {
set.add(interval);
return;
}
if (ceiling.start == val + 1) {
ceiling.start = val;
}
if (floor.end == val - 1) {
floor.end = val;
}
if (floor.end == ceiling.start) {
// 由于是按照左端点排序,所以要删floor。
// 注意TreeSet里删除的时候是调用的比较器判相等的
set.remove(floor);
ceiling.start = floor.start;
}
}
/**
* @return: A list of intervals.
*/
public List<Interval> getIntervals() {
// write your code here
return new ArrayList<>(set).subList(1, set.size() - 1);
}
}
class Interval {
int start, end;
public Interval(int start, int end) {
this.start = start;
this.end = end;
}
}
添加时间复杂度 O ( log n ) O(\log n) O(logn), n n n是已经得到的区间个数,得到全部区间 O ( n ) O(n) O(n),空间 O ( n ) O(n) O(n)。