352. Data Stream as Disjoint Intervals
Given a data stream input of non-negative integers a1, a2, …, an, …, summarize the numbers seen so far as a list of disjoint intervals.
For example, suppose the integers from the data stream are 1, 3, 7, 2, 6, …, then the summary will be:
[1, 1]
[1, 1], [3, 3]
[1, 1], [3, 3], [7, 7]
[1, 3], [7, 7]
[1, 3], [6, 7]
Follow up:
What if there are lots of merges and the number of disjoint intervals are small compared to the data stream’s size?
这是一道比较新的 hard 题目,刚到手会觉得十分繁琐,因为添加数字不是什么难事,困难的是每次需要查找是否存在 merge 的可能性,最直接的方法是看是否有添加的数字 num 的 num-1 或者 num+1的存在。
这方法毫无疑问需要起码 O(\(n^2)\)复杂度,对于大量数字的操作是不利的
根据提示我们了解到 Binary Search Tree是一个有效的方法,其基本过程如下
- 每个节点储存一个 interval
- 每次添加新的 num 时将会检查最近的一个 上限interval 和一个下限 interval 的数字,如果能够接上,则将接上 interval
- 删掉该节点,连接后续节点
因为 TreeMap 不是一个很常用的数据结构,我认为我们应该尽量少使用,多用基本的数据形式。
/**
* 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; }
* }
*/
public class SummaryRanges {
class TreeNode{
Interval val;
TreeNode left;
TreeNode right;
public TreeNode(int num){
this.val = new Interval(num,num);
}
}
TreeNode root;
/** Initialize your data structure here. */
public SummaryRanges() {
}
public void addNum(int val) {
//假如不存在 root,那么新添加的数字将成为 root
if(root == null) root = new TreeNode(val);
else add(root,val);
//其他情况检查是否存在 merge 的 interval
}
public void add(TreeNode root, int val){
//恰好在某个 interval 范围内,不需要任何操作,直接返回
if(root.val.start <= val && root.val.end>= val){
return;
}else if(root.val.start == val + 1){
//如果 num 能够接上 root 当前的 start,那么扩展这个 interval,我们接下来需要查找的是最接近的一个 end 是否能让我们接上整个数字
root.val.start = val;
//now we need to see if child nodes has any merge
TreeNode parent = root;
TreeNode cur = root.left;
while(cur != null && cur.right != null){
parent = cur;
cur = cur.right;
}
if(cur != null && (cur.val.end == root.val.start || cur.val.end +1 == root.val.start)){
root.val.start = cur.val.start;
if(parent == root){
parent.left = cur.left;
}else{
parent.right = cur.left;
}
}
}
else if(root.val.end + 1 == val){
root.val.end = val;
TreeNode parent = root;
TreeNode cur = root.right;
while(cur != null && cur.left != null){
parent = cur;
cur = cur.left;
}
if(cur != null && (cur.val.start == root.val.end|| cur.val.start - 1 == root.val.end)){
root.val.end = cur.val.end;
if(parent == root){
parent.right = cur.right;
}else{
parent.left = cur.right;
}
}
}
//此时我们无法对当前 root node 做任何操作,我们根据数字确定接下来要走的方向
else if(root.val.start > val){
if(root.left ==null ){
root.left = new TreeNode(val);
}else{
add(root.left,val);
}
}
else {
if(root.right == null) root.right = new TreeNode(val);
else{
add(root.right,val);
}
}
}
public List<Interval> getIntervals() {
List<Interval> res = new ArrayList();
traverse(root,res);
return res;
}
public void traverse(TreeNode root,List<Interval> res ){
if(root != null){
traverse(root.left,res);
res.add(root.val);
traverse(root.right,res);
}
}
}
/**
* Your SummaryRanges object will be instantiated and called as such:
* SummaryRanges obj = new SummaryRanges();
* obj.addNum(val);
* List<Interval> param_2 = obj.getIntervals();
*/