16Merge intervalsArrays and hashing

Merge intervals

Overlapping ranges to combine: sort by start, then stretch the last block or start a new one.

Clue: overlapping ranges to combine, such as bookings or time slots. Worked example: LeetCode 56, Merge Intervals (Medium).

The idea

One meeting room, five bookings: when is the room busy? First, sort them by start time. Then walk left to right keeping the last busy block. If the next booking starts at or before that block's end, it's the same stretch: push the end out if needed, with max. If it starts after the block ends, there is a free gap; because everything after starts even later, the block is final, so start a new one.

The walk

Bookings [[13, 17], [9, 11], [18, 19], [14, 15], [10, 12]], sorted by start: [9, 11], [10, 12], [13, 17], [14, 15], [18, 19].

nextlast blockoverlap?blocks after
[9, 11]first[9, 11]
[10, 12][9, 11]10 ≤ 11, yes[9, 12]
[13, 17][9, 12]13 > 12, no[9, 12], [13, 17]
[14, 15][13, 17]14 ≤ 17, yes[9, 12], [13, 17] (end stays 17)
[18, 19][13, 17]18 > 17, no[9, 12], [13, 17], [18, 19]

Quick check from the post: [[2, 4], [1, 3], [6, 8]] → [[1, 4], [6, 8]]. Sorted, it is [1, 3], [2, 4], [6, 8]; 2 is before 3, so they join.

Pseudocode

Pseudocode · any language
function merge(intervals):
    sort intervals by start
    merged = empty array
    add intervals[0] to merged
    for i from 1 to length(intervals) - 1:
        [start, end] = intervals[i]
        last = merged[length(merged) - 1]
        if start <= last[1]:          // overlap
            last[1] = max(last[1], end)
        else:
            add [start, end] to merged
    return merged

Each pair is [start, end], so last[1] is the last block's end.

The trap: keep the max. A booking inside the block must not shrink it. Write last[1] = end instead of max(last[1], end) and [14, 15] inside [13, 17] drops the end to 15: hours 15 to 17 vanish and you get [13, 15].

Code

import java.util.*;

public class Main {
    static int[][] merge(int[][] intervals) {
        Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
        List<int[]> merged = new ArrayList<>();
        merged.add(new int[]{intervals[0][0], intervals[0][1]});
        for (int i = 1; i < intervals.length; i++) {
            int start = intervals[i][0], end = intervals[i][1];
            int[] last = merged.get(merged.size() - 1);
            if (start <= last[1]) {             // overlap
                last[1] = Math.max(last[1], end);
            } else {
                merged.add(new int[]{start, end});
            }
        }
        return merged.toArray(new int[0][]);
    }
}

Complexity

  • Time: O(n log n) for the sort; the sweep after it is one pass, O(n).
  • Extra space: O(n) for the merged list (plus whatever the language's sort uses).

How it was tested

All four programs were compiled and run on 3,014 cases (the slide examples, edge cases such as one interval, identical intervals, touching intervals like [1, 4] and [4, 5], nested intervals, sorted and reversed input, values up to 10,000, plus 3,000 random cases) and every output matched an independent brute force that repeatedly joins any two overlapping intervals until none overlap. Re-run on 31 October 2026 on 3,004 more cases (3,000 random lists of 1 to 8 intervals with values up to 25, plus the example, the quick check, [[1, 4], [4, 5]] and [[1, 1]]): all four matched on every one.

Practice

Try these in order. Open the worked example first if you got stuck on the slides.