Skip to content
Reliable Data Engineering
Practice problem medium intervalsarrays
Practise with timer, notes and rubric

Insert an Interval Into a Sorted Schedule

Pattern: Merge Intervals · Difficulty: Medium · Asked at: Google, LinkedIn, Meta

Classic version: LeetCode 57 · LeetCode 56

Problem

schedule is a list of non-overlapping [start, end] intervals sorted by start (e.g. maintenance windows). Insert new and return the schedule, still sorted and non-overlapping, merging where necessary. Intervals that touch (end == start) are merged.

Examples

insert_interval([[1, 3], [6, 9]], [2, 5])                       → [[1, 5], [6, 9]]
insert_interval([[1, 2], [3, 5], [6, 7], [8, 10], [12, 16]], [4, 8]) → [[1, 2], [3, 10], [12, 16]]

Starter code

def insert_interval(schedule: list[list[int]], new: list[int]) -> list[list[int]]:
    pass

Hints

Hint 1

Everything ending before new starts is untouched; everything starting after new ends is untouched.

Hint 2

In between, all intervals overlap new: grow new to cover them (min start, max end).

Where this shows up in data engineering

Maintaining sorted, non-overlapping ranges is how SCD2 validity windows, partition/offset ranges in ingestion checkpoints, and reserved time slots are updated incrementally. Re-sorting everything on each insert is the naive answer; the linear merge is the production one.

Solution

def insert_interval(schedule, new):
    res, i, n = [], 0, len(schedule)
    start, end = new
    while i < n and schedule[i][1] < start:          # entirely before
        res.append(schedule[i])
        i += 1
    while i < n and schedule[i][0] <= end:           # overlapping: absorb
        start = min(start, schedule[i][0])
        end = max(end, schedule[i][1])
        i += 1
    res.append([start, end])
    res.extend(schedule[i:])                         # entirely after
    return res

Tests

Your solution should pass these:

assert insert_interval([[1, 3], [6, 9]], [2, 5]) == [[1, 5], [6, 9]]
assert insert_interval([[1, 2], [3, 5], [6, 7], [8, 10], [12, 16]], [4, 8]) == [[1, 2], [3, 10], [12, 16]]
assert insert_interval([], [5, 7]) == [[5, 7]]
assert insert_interval([[1, 5]], [6, 8]) == [[1, 5], [6, 8]]
assert insert_interval([[1, 5]], [5, 7]) == [[1, 7]]
assert insert_interval([[3, 5]], [1, 2]) == [[1, 2], [3, 5]]

Explanation

Three phases over the already-sorted input: copy, merge, copy. O(n) time, versus O(n log n) for appending and re-running a full merge.

Overlap test: with sorted input, interval [a, b] overlaps [start, end] when a <= end and b >= start; phase 1 handles the b < start case, so phase 2 only checks a <= end. Changing < to <= (and vice versa) is how you switch between “touching intervals merge” and “touching intervals stay separate”. Clarify that with the interviewer.

Finding the start in O(log n) with binary search is possible, but the output is O(n) anyway.

Follow-up questions

The schedule has millions of intervals and receives frequent inserts. Data structure?

A balanced BST / sorted container keyed by start (e.g. sortedcontainers.SortedList), or an interval tree. Find neighbours in O(log n), merge locally, so each insert is O(log n + merged).