package com.freetymekiyan.algorithms.level.hard;

import java.util.LinkedList;
import java.util.List;

/**
 * Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary).
 * <p>
 * You may assume that the intervals were initially sorted according to their start times.
 * <p>
 * Example 1:
 * Given intervals [1,3],[6,9], insert and merge [2,5] in as [1,5],[6,9].
 * <p>
 * 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].
 * <p>
 * This is because the new interval [4,9] overlaps with [3,5],[6,7],[8,10].
 * <p>
 * Company Tags: LinkedIn, Google, Facebook
 * Tags: Array, Sort
 * Similar Problems: (H) Merge Intervals
 */
public class InsertInterval {

    /**
     * Array. In-place solution.
     * Skip the non-overlapping intervals whose end time is before new interval's start.
     * For overlapped intervals that start before new interval end.
     * | Remove this overlapped interval from list.
     * | Merge it with the new interval by: 1. update start to min start times; 2. update end to max end times.
     * Add new interval in the position i.
     */
    public List<Interval> insert(List<Interval> intervals, Interval newInterval) {
        int i = 0;
        // Skip the non-overlapping intervals.
        while (i < intervals.size() && intervals.get(i).end < newInterval.start) {
            i++;
        }
        while (i < intervals.size() && intervals.get(i).start <= newInterval.end) { // Overlap.
            // Remove the overlapped interval.
            Interval inter = intervals.remove(i);
            // Merge with new interval.
            newInterval.start = Math.min(inter.start, newInterval.start);
            newInterval.end = Math.min(inter.end, newInterval.end);
        }
        intervals.add(i, newInterval); // Insert new interval.
        return intervals;
    }

    /**
     * Array. Sort. Not in place solution. O(n) Time.
     * Make use of intervals are sorted.
     * Create a result list of intervals, res.
     * Add new interval to res first.
     * Go through the given intervals.
     * For each interval i, there are 3 situations:
     * 1) i and previous interval not overlapping, in front of the new interval.
     * 2) No overlap, behind the new interval
     * 3) Overlap, merge with the new interval
     */
    public List<Interval> insertB(List<Interval> intervals, Interval newInterval) {
        List<Interval> res = new LinkedList<>();
        res.add(newInterval);
        for (Interval i : intervals) {
            int start = res.get(res.size() - 1).start; // Last interval.
            int end = res.get(res.size() - 1).end;
            if (i.end < start) { // i ends before last interval.
                res.add(res.size() - 1, i); // Insert it before last interval.
            } else if (end < i.start) { // i starts after last interval.
                res.add(i); // Append it to last.
            } else { // i overlaps with last interval.
                start = Math.min(start, i.start);
                end = Math.max(end, i.end);
                res.set(res.size() - 1, new Interval(start, end));
            }
        }
        return res;
    }

    public class Interval {

        int start;
        int end;

        Interval() {
            start = 0;
            end = 0;
        }

        Interval(int s, int e) {
            start = s;
            end = e;
        }
    }
}

@@ -100,16 +100,46 @@
 t;%0A%0A/**%0A
+ * 57. Insert Interval%0A * %3Cp%3E%0A
  * Given
@@ -1441,32 +1441,114 @@
  i++;%0A        %7D%0A
+        // Now i == intervals.size() OR intervals.get(i).end %3E= newInterval.start%0A
         while (i
@@ -1622,53 +1622,48 @@
  // 
-Overlap.%0A            // Remove the overlapped
+Compare current interval with merged new
  int
@@ -1722,38 +1722,33 @@
 (i);
-%0A            // Merge with new
+ // Remove the overlapped
  int
@@ -1860,26 +1860,26 @@
 end = Math.m
-in
+ax
 (inter.end, 
@@ -1948,32 +1948,8 @@
 al);
- // Insert new interval.
 %0A   

update 57. Insert Interval