Algorithm

Non-overlapping Intervals

Greedy & Intervals Pattern

Non-overlapping Intervals

You are given an array of intervals where each is [start, end]. Return the minimum number of intervals you must remove so that none of the remaining intervals overlap. Intervals that merely touch at an endpoint (one ends exactly where the next begins) are not considered overlapping and may both stay.

CONSTRAINTS
  • 1 ≤ intervals.length ≤ 10⁵
  • intervals[i].length == 2
  • -5 × 10⁴ ≤ start < end ≤ 5 × 10⁴
  • Touching at an endpoint is allowed; only intervals sharing interior points conflict
EXAMPLE 1
Input: intervals = [[1,2],[2,3],[3,4],[1,3]]
Output: 1
Removing [1,3] leaves [1,2], [2,3] and [3,4], which only touch at endpoints and so do not overlap. No single removal other than [1,3] achieves this.
EXAMPLE 2
Input: intervals = [[1,2],[1,2],[1,2]]
Output: 2
All three occupy the same range, so at most one can stay and the other two must go.
EXAMPLE 3
Input: intervals = [[1,2],[2,3]]
Output: 0
The two intervals only touch at the point 2, which is not an overlap, so nothing needs removing.
EXAMPLE 4
Input: intervals = [[1,100],[11,22],[1,11],[2,12]]
Output: 2
Keeping the earliest-finishing intervals [1,11] and [11,22] leaves a disjoint set; the long [1,100] and the overlapping [2,12] are the two removed.
EXAMPLE 5
Input: intervals = [[1,2]]
Output: 0
A single interval cannot overlap anything, so no removals are needed.
Do intervals that touch at an endpoint count as overlapping?
No — [1,2] and [2,3] may both stay. This is the opposite convention from Merge Intervals, and it is why the keep test uses start ≥ last_end. Always confirm the endpoint rule, since it flips that comparison.
Why sort by end time rather than start time?
Because the goal is to keep the maximum number of non-overlapping intervals, and the interval that frees up soonest leaves the most room for the rest. Sorting by start would give the wrong greedy, exactly as it does in Activity Selection.
Is the input sorted?
No, it can be in any order, so you sort it yourself — by end time — as the first step.
What if there are zero or one intervals?
Then nothing can overlap and the answer is 0. The sweep produces this automatically, since the single interval is simply kept.
Flip the question before solving it

"Remove the fewest intervals so the rest do not overlap" sounds like a deletion problem, but the first move is to invert it: removing the fewest is the same as keeping the most. If you can identify the largest set of intervals that already avoid each other, then everything else is what you delete, and its size is minimal by definition.

So the real task is: find the maximum number of mutually non-overlapping intervals, then the answer is total − kept.

And "maximum non-overlapping intervals" is a problem you have already solved — it is exactly Activity Selection. This is that problem wearing a different question.

Which interval to keep among conflicts

When several intervals overlap and you can keep only one, which survives? Run the same test of tempting rules as everywhere in greedy:

- Keep the earliest-starting? No — it might run very long and block many later intervals.
- Keep the shortest? No — a short interval in the wrong place still knocks out two neighbours.
- Keep the earliest-finishing? Yes. Finishing soonest frees up the most room for everyone after it.

This is the Activity Selection greedy, and it is justified by the same exchange argument: among any set of conflicting intervals, replacing whichever the optimal solution kept with the earliest-finishing one never creates a new conflict (it ends no later, so nothing that followed is disturbed) and never reduces the count. So keeping the earliest-finishing interval is always safe.

The algorithm

Sort by end time. Sweep, tracking the end of the last interval you decided to keep. For each interval:

- If it starts at or after that last end, it does not conflict — keep it, and advance the boundary to its end.
- If it starts before that last end, it overlaps the interval you are keeping. Since you are committed to the earlier-finishing one, this later interval must be removed — count it.
python
def erase_overlap_intervals(intervals):
    intervals.sort(key=lambda x: x[1])        # sort by END
    removals = 0
    last_end = float('-inf')
    for start, end in intervals:
        if start >= last_end:                 # no conflict: keep it
            last_end = end
        else:                                 # conflict: remove this one
            removals += 1
    return removals

Sorting is O(N log N); the sweep is O(N).

Crucial Notesort by end, not start — this is the single most important decision, and it is the opposite key from Merge Intervals. Merging asks "what touches what", which sorting by start makes local; selecting the most non-overlapping intervals asks "which frees up soonest", which sorting by end answers. Same family, different question, different key. Also note the conflict test is start >= last_end (keep) versus start < last_end (remove), because touching does not count as overlap here — the reverse of the Merge Intervals convention. Getting either the key or the boundary wrong produces a plausible but wrong count.
Worked Example:[[1,2],[2,3],[3,4],[1,3]]
Sort by end: [1,2], [2,3], [1,3], [3,4].
- [1,2]: start 1 ≥ −∞. Keep. last_end = 2.
- [2,3]: start 2 ≥ 2 (touching is allowed). Keep. last_end = 3.
- [1,3]: start 1 < 3. Conflict — remove. removals = 1.
- [3,4]: start 3 ≥ 3. Keep. last_end = 4.

Answer 1 — remove [1,3], and [1,2],[2,3],[3,4] remain disjoint (touching endpoints and all).

And [[1,2],[1,2],[1,2]]: sorted, keep the first, the next two both start at 1 < last_end 2, so both are removed → 2.

The takeaway

Two ideas to carry forward.

Invert "minimise removals" into "maximise keeps". Many deletion or destruction problems are really selection problems in disguise, and the selection version is often a clean greedy. Whenever you are asked to remove the fewest things to satisfy a property, ask what the largest surviving set looks like — the complement is usually easier to reason about and to prove optimal.

Match the sort key to the question, not to the data type. Both this problem and Merge Intervals take a list of intervals, yet one sorts by end and the other by start, because they ask fundamentally different things — one selects a maximum disjoint subset, the other fuses touching ranges. The interval "pattern" is not one algorithm; it is a family, and choosing the key is choosing the member. When you recognise an interval problem, your first question should be "am I selecting or merging?" — selecting points to sort-by-end and the Activity Selection greedy, merging points to sort-by-start and the anchor sweep.

Together with Activity Selection, this problem shows the same greedy answering two phrasings — most kept, fewest removed — which is worth remembering as a single reusable tool rather than two separate tricks.

Interactive Strategy Visualization

Finish Line Greedy

0
1
2
3
4
5
Intervals (Sorted by End)
[1,2]
[2,3]
[1,3]
[3,4]
Goal: Remove minimum intervals to make the rest non-overlapping.
CORE LOGIC

Always prioritize the interval that ends first. This leaves the most remaining time for other activities.

REMOVAL RULE

If an interval starts before the previous finish line, it MUST be removed.

Exponential Try Every Subset
O(N log N) Time Greedy Sort by End