Skip to content

Coding patternsBlind 75

Insert Interval

The list is already sorted, so one pass splits it into intervals before, overlapping and after the new one. Solved two ways, sort and merge in O(n log n) and a single pass in O(n), with a step-through, code in five languages and the Big O worked out from the code.

You are given a list intervals of n intervals [start, end] (n is the length of intervals). They do not overlap, and they are sorted by start. You are also given one more interval, new_interval.

Insert new_interval into the list. If it overlaps any intervals, merge them into one. Return the new list, still sorted and with no overlaps. Two intervals overlap if they share at least one point, so [1, 3] and [3, 5] overlap.

Example 1

Input: intervals = [[1, 2], [4, 6], [7, 8], [10, 13], [15, 17]], new_interval = [5, 11]

Output: [[1, 2], [4, 13], [15, 17]]

[5, 11] touches [4, 6], [7, 8] and [10, 13]. All four become one interval, [4, 13].

result[1, 2][4, 13][15, 17]024681012141618

Example 2

Input: intervals = [], new_interval = [3, 5]

Output: [[3, 5]]

The list was empty, so the new interval is the whole answer.

Example 3

Input: intervals = [[2, 4], [8, 9]], new_interval = [5, 6]

Output: [[2, 4], [5, 6], [8, 9]]

It overlaps nothing, so it just slides into its place.

  • 0 ≤ n ≤ 10,000
  • 0 ≤ start ≤ end ≤ 100,000, for every interval and for new_interval
  • intervals is sorted by start and has no overlaps.
Do touching intervals overlap?
Yes. [1, 3] and [3, 5] share the point 3, so they merge into [1, 5].
Is the input sorted?
Yes, by start, and with no overlaps. That is what makes one pass enough.
Can the list be empty?
Yes. Then the answer is just the new interval.
May we change the input list?
Building a new list is cleaner, and it does not change the Big O.

Intervals sorted by start, and a question about overlaps: that is the intervals pattern. Since the list is already sorted and has no overlaps, the intervals that touch new_interval sit next to each other. So the list splits into three runs: before it, overlapping it, and after it.

Finding the first overlap with binary search does not help: building the output list still takes O(n).

One pass O(n)

Walk the list once with an index i, in three parts.

  1. Before. While an interval ends before new_interval starts, it cannot touch it. Copy it.
  2. Overlap. While an interval starts before new_interval ends, they overlap. Swallow it: stretch new_interval to cover both. Then add the grown new_interval.
  3. After. Every interval left starts after new_interval ends. Copy them.

Why it is correct: the input is sorted with no overlaps. So once an interval starts after new_interval ends, every later one does too. The overlapping intervals form one run in the middle.

intervals[1, 2][4, 6][7, 8][10, 13][15, 17]new_interval [5, 11]024681012141618
result
empty
  1. result, i = [], 0
  2. while i < len(intervals) and intervals[i][1] < new_interval[0]:
  3. result.append(intervals[i]); i += 1
  4. while i < len(intervals) and intervals[i][0] <= new_interval[1]:
  5. new_interval = [min(new_interval[0], intervals[i][0]), max(new_interval[1], intervals[i][1])]; i += 1
  6. result.append(new_interval)
  7. while i < len(intervals):
  8. result.append(intervals[i]); i += 1
  9. return result

The list is already sorted. Walk it once, in three parts: before the new interval, overlapping it, and after it.

1 / 8
Solution.javasolution.pySolution.cssolution.gosolution.ts
class Solution {
// One pass: copy the intervals before the new one, swallow the ones that
// overlap it, then copy the rest. O(n) time, O(n) for the output.
public int[][] insert(int[][] intervals, int[] newInterval) {
java.util.List<int[]> result = new java.util.ArrayList<>();
int i = 0, intervalsLength = intervals.length;
int start = newInterval[0], end = newInterval[1];
// Ends before the new one starts: it cannot touch it.
while (i < intervalsLength && intervals[i][1] < start) result.add(intervals[i++]);
// Starts before the new one ends (<=, touching counts): merge it in.
while (i < intervalsLength && intervals[i][0] <= end) {
start = Math.min(start, intervals[i][0]);
end = Math.max(end, intervals[i][1]);
i++;
}
result.add(new int[] { start, end });
// The list is sorted, so everything left starts after end.
while (i < intervalsLength) result.add(intervals[i++]);
return result.toArray(new int[0][]);
}
}
Time O(n)Space O(n)Here is how we get these from the code, in four steps.
  1. 1 Find the work that repeats

    There are three loops, one after the other. Each run looks at one interval and copies or swallows it. That is 1 step. All three loops move the same index i.

    1. result, i = [], 0
    2. while i < len(intervals) and intervals[i][1] < new_interval[0]:
    3. result.append(intervals[i]); i += 1
    4. while i < len(intervals) and intervals[i][0] <= new_interval[1]:
    5. new_interval = [min(new_interval[0], intervals[i][0]), max(new_interval[1], intervals[i][1])]; i += 1
    6. result.append(new_interval)
    7. while i < len(intervals):
    8. result.append(intervals[i]); i += 1
    9. return result
  2. 2 Count the runs on Example 1

    Example 1 has 5 intervals. One is before, three overlap, and one is after. Then one append adds the grown interval.

    1 + 3 + 1 = 5 runs, plus 1 append.

    interval[1, 2][4, 6][7, 8][10, 13][15, 17]
    partbeforeoverlapoverlapoverlapafter
    total
    5 runs + 1 append
  3. 3 Write the count for any n

    The three loops share i, and i goes from 0 to n once in total:

    n runs + 1 append

    Check it with Example 1: 5 + 1 = 6.

  4. 4 Keep only the part that grows fastest

    Drop the + 1. What is left is n. Three loops in a row are still one pass.

    So the time is O(n).

  5. S Space: count the extra memory the same way

    The result holds up to n + 1 intervals, so the space is O(n). Beyond the output, the code only keeps i and new_interval.

    result (up to n + 1)inew_interval

2,000 random lists of 0 to 5 sorted intervals with small gaps, and a new interval that often touches an end, sits in a gap, covers several intervals, or comes before or after all of them. Sort and merge and one pass gave the same answer every time.

Largest inputsResult
10,000 intervals (the most allowed), with the new interval covering the middle half. One pass.Under 0.001 s, and about 0.06 MB of extra memory: the result list of 5,000 intervals.
The same 10,000 intervals. Sort and merge.0.001 s, and about 0.5 MB: it copies every interval before sorting.