DSA Guide
Intervals

Insert Interval

Add one interval to a sorted list and merge what it touches, in three clean phases instead of one tangled loop

MediumIntervals· three-phase scan
ArrayIntervals
Problem #57

Problem Statement

You are given a list of non-overlapping intervals sorted by start time, and one new interval. Insert it, merging with anything it overlaps, and keep the result sorted and non-overlapping.

Input:  intervals = [[1,3], [6,9]],  new = [2,5]
Output: [[1,5], [6,9]]
        [2,5] overlaps [1,3] → merged into [1,5]

Input:  intervals = [[1,2], [3,5], [6,7], [8,10], [12,16]],  new = [4,8]
Output: [[1,2], [3,10], [12,16]]
        [4,8] swallows [3,5], [6,7] and [8,10]

Intuition

The lazy solution works: append the new interval, sort, and run Merge Intervals. That is O(n log n).

But the input is already sorted and already non-overlapping. Sorting again throws away a guarantee you were handed for free. With it, the answer is O(n).

The insight

The sorted list splits into exactly three regions relative to the new interval:

intervals:  [1,2]   [3,5]  [6,7]  [8,10]     [12,16]
new:                  [4 ─────────── 8]

            └ BEFORE ┘└─── OVERLAPPING ───┘  └ AFTER ┘
             ends before        touches       starts after
             new starts         new           new ends

Before — copy across untouched. Overlapping — absorb into one widened interval. After — copy across untouched.

Because the list is sorted, these regions are contiguous. You never go back.

Writing this as three separate loops is far clearer than one loop with a state flag — and it removes the class of bug where "am I still merging?" gets out of sync.

Approach

Phase 1 — copy everything that ends before the new interval starts

While intervals[i].end < new.start, this interval cannot touch the new one. Copy it and move on.

Phase 2 — absorb everything that overlaps

While intervals[i].start <= new.end, there is overlap. Widen the new interval to cover both:

new.start = min(new.start, intervals[i].start)
new.end   = max(new.end,   intervals[i].end)

Then append the widened interval once, after the loop.

Phase 3 — copy the rest

Everything remaining starts after the new interval ends. Copy it across.

[[1,2], [3,5], [6,7], [8,10], [12,16]] inserting [4,8]
0
[1,2]
i
1
[3,5]
2
[6,7]
3
[8,10]
4
[12,16]
phase1 — beforetest2 < 4 ✓output[[1,2]]
[1,2] ends at 2, before the new interval starts at 4. It can never be touched — copy it straight across.
1 / 6
The three phases run in order and never overlap, because the input was sorted to begin with.

Solution

def insert(
    intervals: list[list[int]], new_interval: list[int]
) -> list[list[int]]:
    """Insert an interval into a sorted, non-overlapping list.

    Three phases: copy what ends too early, absorb what overlaps,
    copy the rest. O(n) — the input's sorted order is reused.
    """
    result: list[list[int]] = []
    start, end = new_interval
    i, n = 0, len(intervals)

    # Phase 1 — entirely before the new interval.
    while i < n and intervals[i][1] < start:
        result.append(intervals[i])
        i += 1

    # Phase 2 — every overlapping interval widens the new one.
    while i < n and intervals[i][0] <= end:
        start = min(start, intervals[i][0])
        end = max(end, intervals[i][1])
        i += 1
    result.append([start, end])

    # Phase 3 — entirely after.
    while i < n:
        result.append(intervals[i])
        i += 1

    return result

The new interval is appended once, outside the loop

A common bug is appending inside phase 2, producing one copy per merge. The merged interval is a single result no matter how many originals it swallowed.

Appending after the loop also handles the case where phase 2 never runs — the new interval simply goes in at the right position, unmerged.

Complexity Analysis

Time Complexity

O(n)

Space Complexity

O(n)

Timei only ever moves forward, so all three loops together visit each interval once. Compare with the sort-and-merge approach at O(n log n).

SpaceO(n) for the output. No extra structure.

Edge Cases

On this page