LeetCode Β· #435

Non-overlapping Intervals Solution

Return the minimum number of intervals to remove so the rest are non-overlapping.

01
Section One Β· Problem

Problem Statement

🏷️

Difficulty

Medium

πŸ”—

LeetCode

Problem #435

πŸ—οΈ

Pattern

Intervals β€” greedy sort by end

Given array of intervals, return the minimum number of intervals you need to remove to make the rest non-overlapping.

Example
Input: intervals = [[1,2],[2,3],[3,4],[1,3]] Output: 1 // Remove [1,3]. Remaining: [1,2],[2,3],[3,4] β€” non-overlapping.
Constraints
β€’ 1 ≀ intervals.length ≀ 10⁡ β€’ intervals[i].length == 2 β€’ -5 Γ— 10⁴ ≀ startα΅’ < endα΅’ ≀ 5 Γ— 10⁴
02
Section Two Β· Approach 1

DP β€” O(nΒ²)

Sort by start. dp[i] = max non-overlapping intervals ending at i. For each i, check all j < i.

Problem: O(nΒ²). The classic interval scheduling greedy: sort by end time, greedily keep the earliest-ending non-overlapping interval. Removals = total βˆ’ kept.
Java β€” Greedy (sort by end β€” this IS optimal)
import java.util.*; class Solution { public int eraseOverlapIntervals(int[][] intervals) { Arrays.sort(intervals, (a,b) -> a[1] - b[1]); int kept = 1, end = intervals[0][1]; for (int i = 1; i < intervals.length; i++) { if (intervals[i][0] >= end) { kept++; end = intervals[i][1]; } } return intervals.length - kept; } }
Python β€” Greedy (sort by end)
class Solution: def eraseOverlapIntervals(self, intervals: list[list[int]]) -> int: intervals.sort(key=lambda x: x[1]) kept, end = 1, intervals[0][1] for s, e in intervals[1:]: if s >= end: kept += 1 end = e return len(intervals) - kept
03
Section Three Β· Approach 2

Greedy Sort-by-End β€” O(n log n)

Sort by end time. Greedily keep each interval whose start β‰₯ the last kept interval's end. The number of intervals NOT kept = removals needed.

πŸ’‘ Mental model: This is the classic activity selection problem from greedy algorithms. Imagine scheduling as many meetings as possible in one room. Sort by end time. Always pick the meeting that finishes earliest and doesn't conflict. This maximizes the number of meetings (= minimum removals).
Algorithm
  • Sort intervals by end ascending.
  • Initialize kept = 1, end = intervals[0].end.
  • For each interval: if start >= end β†’ keep it, update end. Else skip (removed).
  • Return n - kept.
🎯 When to recognize this pattern: "Minimum removals to make intervals non-overlapping" = "Maximum non-overlapping intervals you can keep." This is interval scheduling maximization. Always sort by end (not start!). The exchange argument proves greedy correctness: earliest-ending intervals leave the most room for future selections.
Why sort by end, not start? Sorting by start groups intervals that begin together but may have vastly different lengths. Sorting by end selects the interval that finishes earliest β€” freeing the timeline for more intervals. [1,100] starts early but hogs the timeline; [1,2] ends early and is a better greedy choice.
04
Section Four Β· Trace

Visual Walkthrough

Trace for intervals = [[1,2],[2,3],[3,4],[1,3]].

Non-overlapping Intervals β€” Sort by End
Sorted by end: [1,2],[2,3],[1,3],[3,4]. Greedy: keep earliest-ending non-overlapping. [1,2]: keep. end=2. kept=1. [2,3]: start=2 β‰₯ end=2 β†’ keep. end=3. kept=2. [1,3]: start=1 < end=3 β†’ skip (remove). [3,4]: start=3 β‰₯ end=3 β†’ keep. kept=3. Removals = 4-3 = 1. return 1 βœ“
05
Section Five Β· Implementation

Code β€” Java & Python

The brute force section already contains the optimal greedy code (it IS the clean approach). See Section 02 for complete Java and Python implementations.

06
Section Six Β· Analysis

Complexity Analysis

ApproachTimeSpaceTrade-off
DP (LIS-variant)O(nΒ²)O(n)dp[i] = max chain ending at i
Greedy sort-by-end ← optimalO(n log n)O(1)Classic activity selection; single pass after sort
07
Section Seven Β· Edge Cases

Edge Cases & Pitfalls

CaseInputWhy It Matters
No overlap[[1,2],[3,4]]Remove 0.
All overlap[[1,4],[2,3],[3,5]]Keep [2,3] and [3,5]. Remove 1.
Single interval[[1,2]]Remove 0.
Touching[[1,2],[2,3]]start=end β†’ not overlapping. Remove 0.
Negative values[[-2,0],[0,2]]Works the same. Sort + greedy.
⚠ Common Mistake: Sorting by start instead of end. Sorting by start and greedily keeping the first non-overlapping interval does NOT maximize the number kept. Example: [[1,100],[2,3],[3,4]]. Sort by start: keep [1,100], skip rest β†’ kept=1. Sort by end: keep [2,3],[3,4] β†’ kept=2. Removing 1 is better than removing 2.

← Back to Interval problems