DSA Trainer
← All patterns
Pattern

Intervals Pattern

Sort by start. Compare ends. Merge or commit with one condition.

Quick Summary

Best used when

  • You're working with [start, end] pairs that may overlap
  • You need to merge overlapping intervals into one non-overlapping set
  • You need to detect whether any two intervals conflict
  • You need to insert a new interval into a sorted list and re-merge

Clue words in the problem

intervalsmeetingoverlapmergeschedulestartendconflictnon-overlapping

Complexity gain

O(n²) pairwise overlap checks → O(n log n) sort once, O(n) scan

Why this pattern matters for LeetCode

The brute force compares every interval against every other interval to find overlaps — O(n²). Once you sort by start time, you get a guarantee that eliminates almost all of those comparisons: after sorting, the only interval that can overlap with the current one is the immediately next one in the list. Everything earlier has already been processed. One O(n) scan after the O(n log n) sort handles every case.

What the Intervals pattern is

Most interval problems share the same first step: sort by start time. Sorting puts all potentially overlapping intervals next to each other — if two intervals don't overlap after sorting, nothing that comes later can change that. After sorting, scan left to right and compare the start of the current interval to the end of the last merged interval. If they overlap (current.start ≤ last.end), extend last.end to the maximum of both ends. If they don't (current.start > last.end), commit the current interval as its own entry. That single comparison handles every case.

Reach for Intervals when you catch yourself thinking…

These are the internal questions that signal this pattern.

Am I working with [start, end] pairs that might overlap?
Do I need to find and merge all groups of overlapping intervals?
Does the brute force compare every interval against every other?
Am I scheduling tasks or meetings and checking for conflicts?

Beginner mental model

Think of scheduling meetings on a whiteboard timeline. You sort all meetings by start time, then walk down the list. If the next meeting starts before the current block ends, they overlap — extend the block to cover both. If the next meeting starts after the current block ends, draw a gap and start a new block. You never need to look further back than the block you just drew. That's Merge Intervals.

Ready to practice Intervals?

DSA Trainer gives you guided hints, not answers, so you build real intuition.

When to use Intervals

  • Merging a list of overlapping intervals into the minimal non-overlapping set (Merge Intervals)
  • Inserting a new interval into a sorted list and re-merging any overlaps it creates (Insert Interval)
  • Finding the minimum number of intervals to remove so none overlap (Non-Overlapping Intervals)
  • Detecting whether any two intervals in a set conflict (Meeting Rooms)
  • Counting the peak number of concurrent intervals — how many overlap at one point (Meeting Rooms II)

When NOT to use Intervals

  • The intervals are already non-overlapping and you're just querying them — binary search may be sufficient
  • The problem is about point queries on ranges rather than merging — prefix sums or a segment tree may apply
  • Intervals are continuously added or removed — a sorted set or sweep-line handles dynamic updates more cleanly
  • The input is small enough that an O(n²) brute force is fast enough and simpler to write

Better alternative: In those cases, a sorted data structure or sweep-line algorithm handles dynamic interval problems more cleanly.

JavaScript code template

Merge Intervals — O(n log n) time, O(n) space

function merge(intervals) {
  intervals.sort((a, b) => a[0] - b[0]);

  const result = [intervals[0]];

  for (let i = 1; i < intervals.length; i++) {
    const last = result[result.length - 1];
    const current = intervals[i];

    if (current[0] <= last[1]) {
      // Overlap: extend the end if current reaches further
      last[1] = Math.max(last[1], current[1]);
    } else {
      // No overlap: commit current as a new entry
      result.push(current);
    }
  }

  return result;
}

Sort by start time first — always step one. Seed the result with the first interval. For each remaining interval: get the last entry in result (the one you're potentially merging into) and compare current[0] to last[1]. If current starts at or before last ends, overlap — set last[1] = Math.max(last[1], current[1]) because current might extend further. If current starts past last ends, no overlap — push current as a new entry. One pass covers everything.

Step-by-step thinking process

Walk through this in your head before writing code.

  1. 1Sort the intervals by start time — always the first step.
  2. 2Seed the result array with intervals[0].
  3. 3For each subsequent interval, get the last interval in result (result[result.length - 1]).
  4. 4If current[0] <= last[1] — overlap. Set last[1] = Math.max(last[1], current[1]).
  5. 5If current[0] > last[1] — no overlap. Push current into result.
  6. 6Return result.

LeetCode-style problems that use this pattern

Practice on DSA Trainer with guided hints: no spoilers, just nudges.

Coming soon

  • Merge Intervals

    Sort by start, then scan: extend the last merged interval's end or commit a new one.

    Coming soon
  • Insert Interval

    Walk through a sorted list: copy intervals that end before the new one starts, merge all that overlap with it, then copy the rest.

    Coming soon
  • Non-Overlapping Intervals

    Greedy: sort by end time and always keep the interval that finishes earliest — count how many you remove.

    Coming soon
  • Meeting Rooms

    Sort by start. If any interval starts before the previous one ends, there's a conflict — return false.

    Coming soon
  • Meeting Rooms II

    Count the peak number of intervals overlapping at the same time — that's the minimum number of rooms needed.

    Coming soon

Common mistakes beginners make

  • Forgetting to sort first — without sorted order, non-adjacent intervals can overlap and a single pass will miss them.
  • Setting last[1] = current[1] instead of Math.max(last[1], current[1]) — a fully contained interval (both start and end inside last) would incorrectly shrink the merged interval.
  • Using strict less-than (current[0] < last[1]) instead of <= — touching intervals like [1, 2] and [2, 3] should merge into [1, 3]; strict less-than leaves them separate.
  • Comparing to the wrong interval — always compare to result[result.length - 1], not to intervals[i - 1]. You're merging into the result, not back into the original list.

Intervals vs Two Pointers

Choose the right tool for the job.

PatternUse when…
IntervalsYou sort [start, end] pairs and scan once to merge overlapping ranges — sorting guarantees only adjacent intervals need comparison
Two PointersYou compare values at two positions in a structured input and move them based on comparisons — not about ranges overlapping

Time and space complexity

Time complexity

O(n log n)

Space complexity

O(n)

Sorting dominates at O(n log n). The single scan after sorting visits each interval exactly once — O(n). The result array holds at most n intervals if nothing overlaps — O(n) space. Sorting may also use O(log n) stack space internally. Overall: O(n log n) time, O(n) space.

Ready to practice Intervals?

DSA Trainer gives you guided hints, not answers, so you build real intuition.

Frequently asked questions

Why sort by start time?
Sorting by start time guarantees that only adjacent intervals can overlap. If two intervals don't overlap after sorting, no interval that comes later can overlap with the earlier one — it starts even later. This means you only need to compare each interval to the last one in your result. No looking back further.
Why Math.max on the end instead of just using current[1]?
Because one interval can be fully contained inside another. If last = [1, 10] and current = [3, 5], current[1] (5) is smaller than last[1] (10). Assigning last[1] = 5 would shrink the merged interval incorrectly. Math.max(10, 5) = 10, which preserves the full merged range.
Should the overlap check use < or <=?
Use <=. Two intervals that share a single boundary point — like [1, 2] and [2, 3] — are considered overlapping and should merge into [1, 3] in most LeetCode problems. Strict less-than would leave them separate. Check the problem statement, but <= is almost always correct.
How is Insert Interval different from Merge Intervals?
Merge Intervals starts with an unsorted list and sorts it. Insert Interval gives you an already sorted, non-overlapping list and asks you to splice in one new interval, then re-merge any overlaps it creates. The pattern: copy intervals that end before the new one starts, merge everything that overlaps with the new interval into one, then copy the rest unchanged.

Explore other patterns