Skip to content

Merge Intervals Pattern

Overview

Process overlapping intervals by sorting and merging/comparing adjacent intervals. Essential for scheduling, resource allocation, and range problems.

When to Use

  • Merging overlapping intervals
  • Finding intersections between interval lists
  • Inserting into sorted intervals
  • Meeting room scheduling
  • Finding gaps/free slots
  • Interval coverage problems

Merge Intervals Step-by-Step

Interval Relationships: 1. No Overlap: a ends before b starts → keep separate 2. Overlap: a ends after b starts → merge to [a.start, max(a.end, b.end)] 3. Complete Overlap: one contains the other → keep the larger 4. Adjacent: a ends where b starts → optionally merge

Template Code

Merge Overlapping Intervals

public int[][] merge(int[][] intervals) {
    if (intervals.length <= 1) return intervals;

    // Sort by start time
    Arrays.sort(intervals, (a, b) -> a[0] - b[0]);

    List<int[]> merged = new ArrayList<>();
    int[] current = intervals[0];

    for (int i = 1; i < intervals.length; i++) {
        if (intervals[i][0] <= current[1]) {
            // Overlap - extend current interval
            current[1] = Math.max(current[1], intervals[i][1]);
        } else {
            // No overlap - add current and start new
            merged.add(current);
            current = intervals[i];
        }
    }
    merged.add(current);  // Don't forget last interval

    return merged.toArray(new int[merged.size()][]);
}

Insert Interval

public int[][] insert(int[][] intervals, int[] newInterval) {
    List<int[]> result = new ArrayList<>();
    int i = 0;
    int n = intervals.length;

    // Add all intervals before newInterval
    while (i < n && intervals[i][1] < newInterval[0]) {
        result.add(intervals[i]);
        i++;
    }

    // Merge overlapping intervals
    while (i < n && intervals[i][0] <= newInterval[1]) {
        newInterval[0] = Math.min(newInterval[0], intervals[i][0]);
        newInterval[1] = Math.max(newInterval[1], intervals[i][1]);
        i++;
    }
    result.add(newInterval);

    // Add remaining intervals
    while (i < n) {
        result.add(intervals[i]);
        i++;
    }

    return result.toArray(new int[result.size()][]);
}

Interval List Intersections

public int[][] intervalIntersection(int[][] A, int[][] B) {
    List<int[]> result = new ArrayList<>();
    int i = 0, j = 0;

    while (i < A.length && j < B.length) {
        // Find intersection
        int start = Math.max(A[i][0], B[j][0]);
        int end = Math.min(A[i][1], B[j][1]);

        if (start <= end) {
            result.add(new int[]{start, end});
        }

        // Move pointer with smaller end
        if (A[i][1] < B[j][1]) {
            i++;
        } else {
            j++;
        }
    }

    return result.toArray(new int[result.size()][]);
}

Meeting Rooms II (Min Rooms Needed)

public int minMeetingRooms(int[][] intervals) {
    if (intervals.length == 0) return 0;

    int[] starts = new int[intervals.length];
    int[] ends = new int[intervals.length];

    for (int i = 0; i < intervals.length; i++) {
        starts[i] = intervals[i][0];
        ends[i] = intervals[i][1];
    }

    Arrays.sort(starts);
    Arrays.sort(ends);

    int rooms = 0, endPtr = 0;

    for (int i = 0; i < starts.length; i++) {
        if (starts[i] < ends[endPtr]) {
            rooms++;  // Need new room
        } else {
            endPtr++;  // Reuse freed room
        }
    }

    return rooms;
}

// Alternative with Min-Heap
public int minMeetingRoomsHeap(int[][] intervals) {
    if (intervals.length == 0) return 0;

    Arrays.sort(intervals, (a, b) -> a[0] - b[0]);

    PriorityQueue<Integer> heap = new PriorityQueue<>();  // End times

    for (int[] interval : intervals) {
        if (!heap.isEmpty() && heap.peek() <= interval[0]) {
            heap.poll();  // Room freed up
        }
        heap.offer(interval[1]);  // Book the room
    }

    return heap.size();
}

Meeting Rooms (Can Attend All)

public boolean canAttendMeetings(int[][] intervals) {
    Arrays.sort(intervals, (a, b) -> a[0] - b[0]);

    for (int i = 1; i < intervals.length; i++) {
        if (intervals[i][0] < intervals[i-1][1]) {
            return false;  // Overlap found
        }
    }
    return true;
}

Employee Free Time

public List<Interval> employeeFreeTime(List<List<Interval>> schedule) {
    List<Interval> allIntervals = new ArrayList<>();

    // Flatten all intervals
    for (List<Interval> employee : schedule) {
        allIntervals.addAll(employee);
    }

    // Sort by start time
    allIntervals.sort((a, b) -> a.start - b.start);

    List<Interval> result = new ArrayList<>();
    int prevEnd = allIntervals.get(0).end;

    for (int i = 1; i < allIntervals.size(); i++) {
        Interval curr = allIntervals.get(i);

        if (curr.start > prevEnd) {
            // Gap found - free time!
            result.add(new Interval(prevEnd, curr.start));
        }
        prevEnd = Math.max(prevEnd, curr.end);
    }

    return result;
}

Non-overlapping Intervals (Min Removals)

public int eraseOverlapIntervals(int[][] intervals) {
    if (intervals.length == 0) return 0;

    // Sort by end time (greedy: keep shorter intervals)
    Arrays.sort(intervals, (a, b) -> a[1] - b[1]);

    int removals = 0;
    int prevEnd = intervals[0][1];

    for (int i = 1; i < intervals.length; i++) {
        if (intervals[i][0] < prevEnd) {
            removals++;  // Overlap - remove current
        } else {
            prevEnd = intervals[i][1];
        }
    }

    return removals;
}

Minimum Number of Arrows to Burst Balloons

public int findMinArrowShots(int[][] points) {
    if (points.length == 0) return 0;

    // Sort by end position
    Arrays.sort(points, (a, b) -> Integer.compare(a[1], b[1]));

    int arrows = 1;
    int arrowPos = points[0][1];

    for (int i = 1; i < points.length; i++) {
        if (points[i][0] > arrowPos) {
            // Need new arrow
            arrows++;
            arrowPos = points[i][1];
        }
    }

    return arrows;
}

Time & Space Complexity

Problem Time Space
Merge intervals O(n log n) O(n)
Insert interval O(n) O(n)
Interval intersection O(n + m) O(n + m)
Meeting rooms II O(n log n) O(n)
Non-overlapping O(n log n) O(1)

Common Interview Questions

  1. Merge Intervals - Combine overlapping intervals
  2. Insert Interval - Insert and merge if needed
  3. Interval List Intersections - Find all intersections
  4. Meeting Rooms - Can attend all meetings?
  5. Meeting Rooms II - Min rooms needed
  6. Non-overlapping Intervals - Min removals for no overlap
  7. Minimum Number of Arrows - Burst overlapping balloons
  8. Employee Free Time - Find common free time
  9. Add Bold Tag in String - Merge bold intervals
  10. Range Module - Add/remove/query ranges
  11. My Calendar I, II, III - Book without K overlaps
  12. Car Pooling - Can complete all trips?

Tips & Tricks

  • Always sort by start time (or end time for greedy)
  • Sorting by end time often helps minimize selections
  • Use min-heap for tracking "currently active" intervals
  • Two-pointer works for intersection of sorted lists
  • For "min removals": sort by end, greedily keep non-overlapping
  • Watch for edge cases: adjacent intervals, single interval
  • Integer overflow: use Integer.compare() for sorting