package com.interview.leetcode.arrays;
import com.interview.leetcode.utils.Interval;
import java.util.*;
/**
* Created_By: stefanie
* Date: 14-11-14
* Time: 上午7:20
*
* Given a collection of intervals:
* 1. merge all overlapping intervals. {@link #merge}
* For example, given [1,3],[2,6],[8,10],[15,18], return [1,6],[8,10],[15,18].
* 2. the given intervals are non-overlapping, insert a new interval into the intervals (merge if necessary). {@link #merge}
* You may assume that the intervals were initially sorted according to their start times.
* Example 1: given intervals [1,3],[6,9], insert and merge [2,5] in as [1,5],[6,9].
* Example 2: given [1,2],[3,5],[6,7],[8,10],[12,16], insert and merge [4,9] in as [1,2],[3,10],[12,16].
* This is because the new interval [4,9] overlaps with [3,5],[6,7],[8,10].
* 3. given a interval, search if there is a interval in the collection cover the given interval.
* Example 1: given [1,2],[3,10],[12,16], search [4,6] should return true, search [4,15] should return false.
*
* Tricks:
* 1. using start point, to sort or partition the interval.
* 2. define a clear case when will overlap and when would not
* overlap: cur.start <= it.end || cur.end >= it.start
* no overlap: cur.start > it.end || cur.end < it.start
* 3. binary search tree extension for intervals
*/
public class IntervalOperation {
static Comparator<Interval> comparator = new Comparator<Interval>(){
@Override
public int compare(Interval o1, Interval o2) {
if(o1.start == o2.start) return o1.end - o2.end;
else return o1.start - o2.start;
}
};
/**
* 1. at first, sort the interval based on start point
* 2. keep a current interval, and if have overlap (next.start <= cur.end), update cur.end to the max(cur.end, next.end)
* if no overlap found, add cur to merged list and assign next to cur.
*/
public static List<Interval> merge(List<Interval> intervals) {
List<Interval> merged = new ArrayList<Interval>();
if(intervals == null || intervals.size() == 0) return merged;
Collections.sort(intervals, comparator);
Interval current = intervals.get(0);
for(int i = 1; i < intervals.size(); i++){
Interval interval = intervals.get(i);
if(interval.start <= current.end) current.end = Math.max(current.end, interval.end);
else {
merged.add(current);
current = interval;
}
}
merged.add(current);
return merged;
}
/**
* iterate on the intervals,
* 1. no overlapping and place before newInterval (cur.end < newInterval.start) continue
* 2. have overlapping, update newInterval.start to min and newInterval.end to max and remove cur from list
* 3. no overlapping and place after newInterval (cur.start > newInterval.end), so should place newInterval before cur
* Remember to check the newInterval as the last element
*/
public static List<Interval> insert(List<Interval> intervals, Interval newInterval) {
if(intervals == null) intervals = new ArrayList<Interval>();
int offset = 0;
Iterator<Interval> itr = intervals.iterator();
while(itr.hasNext()){
Interval cur = itr.next();
if(cur.end < newInterval.start) offset++;
else if(cur.start > newInterval.end) break;
else {
newInterval.start = Math.min(newInterval.start, cur.start);
newInterval.end = Math.max(newInterval.end, cur.end);
itr.remove();
}
}
if(offset >= intervals.size()) intervals.add(newInterval);
else intervals.add(offset, newInterval);
return intervals;
}
/**
* create a interval binary search tree
* creating the tree by hold the maxEnd in its sub-tree
* during binarysearch, if left.maxEnd > target.end, it should have a sol in a node in left can cover it.
* else search in right
* build tree: O(nlogn)
* binarysearch: O(lgn)
*/
public static Interval search(List<Interval> intervals, Interval target){
IntervalBSTNode root = createIntervalBST(intervals);
return search(root, target);
}
static class IntervalBSTNode{
Interval interval;
IntervalBSTNode left;
IntervalBSTNode right;
int maxEnd;
IntervalBSTNode(Interval interval) {
this.interval = interval;
this.maxEnd = interval.end;
}
}
protected static IntervalBSTNode createIntervalBST(List<Interval> intervals) {
IntervalBSTNode root = null;
for(Interval interval : intervals) root = insert(root, interval);
return root;
}
protected static IntervalBSTNode insert(IntervalBSTNode node, Interval interval){
if(node == null) return new IntervalBSTNode(interval);
if(interval.start == node.interval.start && interval.end == node.interval.end) return node;
else if(interval.start <= node.interval.start) node.left = insert(node.left, interval);
else node.right = insert(node.right, interval);
if(node.maxEnd < interval.end) node.maxEnd = interval.end;
return node;
}
protected static Interval search(IntervalBSTNode node, Interval interval){
if(node == null) return null;
else if(node.interval.start <= interval.start && node.interval.end >= interval.end) return node.interval;
else if(node.left != null && node.left.maxEnd >= interval.end) return search(node.left, interval);
return search(node.right, interval);
}
class MissingRange {
public List<String> findMissingRanges(int[] vals, int start, int end) {
List<String> miss = new ArrayList<>();
if(vals == null || vals.length == 0){
miss.add(getRange(start, end));
return miss;
}
if(vals[0] != start) miss.add(getRange(start, vals[0] - 1));
for(int i = 1; i < vals.length; i++){
if(vals[i] == vals[i - 1] + 1) continue;
miss.add(getRange(vals[i - 1] + 1, vals[i] - 1));
}
if(vals[vals.length - 1] != end) miss.add(getRange(vals[vals.length - 1] + 1, end));
return miss;
}
public String getRange(int begin, int end){
return begin == end? begin + "" : begin + "->" + end;
}
}
}