[Medium] 435. Non-overlapping Intervals
Given an array of intervals intervals where intervals[i] = [starti, endi], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
Note that intervals that only touch at the boundary (e.g., [1,2] and [2,3]) are considered non-overlapping.
Examples
Example 1:
Input: intervals = [[1,2],[2,3],[3,4],[1,3]]
Output: 1
Explanation: [1,3] can be removed and the rest of the intervals are non-overlapping.
Example 2:
Input: intervals = [[1,2],[1,2],[1,2]]
Output: 2
Explanation: You need to remove two [1,2] to make the rest of the intervals non-overlapping.
Example 3:
Input: intervals = [[1,2],[2,3]]
Output: 0
Explanation: You don't need to remove any of the intervals since they're already non-overlapping.
Constraints
1 <= intervals.length <= 10^5intervals[i].length == 2-5 * 10^4 <= starti < endi <= 5 * 10^4
Thinking Process
Given an array of intervals intervals where intervals[i] = [starti, endi], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
Note that intervals that only touch at the boundary (e.g., [1,2] and [2,3]) are considered non-overlapping.
- Define state: what subproblem does
dp[i](ordp[i][j]) represent? - Recurrence: how does the answer build from smaller indices?
- Base cases first; optimize space if only prior row/layer is needed.
Common Approaches
Typical techniques for this pattern:
| Approach | Time | Space | Notes |
|---|---|---|---|
| 1D DP (this problem) | O(n) | O(n) or O(1) | Linear recurrence |
| 2D DP | O(nm) | O(nm) or O(n) | Grid or two-sequence problems |
| State machine DP | O(n) | O(1) | Buy/sell, hold/not-hold states |
| Memoization (top-down) | Same as DP | O(n) | Recursive + cache |
Solution
Solution: Greedy with End Time Sorting
class Solution:
def eraseOverlapIntervals(self, intervals):
if not intervals:
return 0
intervals.sort(key=lambda x: x[1])
N = len(intervals)
right = intervals[0][1]
removals = 0
for i in range(1, N):
if intervals[i][0] < right:
removals += 1
else:
right = intervals[i][1]
return removals
Solution Explanation
Approach: 1D DP (this problem)
Key idea: Given an array of intervals intervals where intervals[i] = [starti, endi], return the minimum number of intervals you need to remove to make the rest of the intervals non-overlapping.
How the code works:
- Define state: what subproblem does
dp[i](ordp[i][j]) represent? - Recurrence: how does the answer build from smaller indices?
- Base cases first; optimize space if only prior row/layer is needed.
Walkthrough — input intervals = [[1,2],[2,3],[3,4],[1,3]], expected output 1:
[1,3] can be removed and the rest of the intervals are non-overlapping.
Time: - Sorting: O(n log n) where n is the number of intervals · Space: O(1)
Algorithm Explanation:
- Edge Case (Line 4):
- If intervals array is empty, return 0 (no removals needed)
- Sort by End Time (Lines 6-8):
- Sort intervals by their end time (
u[1] < v[1]) - This ensures we process intervals with earlier end times first
- Key: Sorting by end time allows greedy choice to be optimal
- Sort intervals by their end time (
- Initialize (Lines 10-11):
right: End time of the last kept interval- Initialize with first interval’s end time (we always keep the first interval)
removals: Counter for number of intervals to remove
- Process Remaining Intervals (Lines 12-18):
- For each interval starting from index 1:
- If overlaps:
intervals[i][0] < right- Current interval’s start is before last kept interval’s end
- Increment
removals(we remove this overlapping interval)
- Else: No overlap
- Update
rightto current interval’s end time - Keep this interval (don’t increment removals)
- Update
- If overlaps:
- For each interval starting from index 1:
Example Walkthrough:
Example 1: intervals = [[1,2],[2,3],[3,4],[1,3]]
After sorting by end time:
Original: [[1,2],[2,3],[3,4],[1,3]]
Sorted: [[1,2], [2,3], [1,3], [3,4]]
[1,2] end=2
[2,3] end=3
[1,3] end=3
[3,4] end=4
Execution:
Initial: right = 2 (from [1,2]), removals = 0
i=1: [2,3]
Check: 2 < 2? No (no overlap)
Keep: right = 3, removals = 0
i=2: [1,3]
Check: 1 < 3? Yes (overlaps with [2,3])
Remove: removals = 1
i=3: [3,4]
Check: 3 < 3? No (no overlap, boundary touch is OK)
Keep: right = 4, removals = 1
Result: 1 removal
Kept intervals: [1,2], [2,3], [3,4]
Example 2: intervals = [[1,2],[1,2],[1,2]]
After sorting by end time:
All intervals have same end time: [[1,2], [1,2], [1,2]]
Execution:
Initial: right = 2 (from first [1,2]), removals = 0
i=1: [1,2]
Check: 1 < 2? Yes (overlaps)
Remove: removals = 1
i=2: [1,2]
Check: 1 < 2? Yes (overlaps)
Remove: removals = 2
Result: 2 removals
Kept intervals: [1,2] (only the first one)
Example 3: intervals = [[1,2],[2,3]]
After sorting by end time:
Already sorted: [[1,2], [2,3]]
Execution:
Initial: right = 2 (from [1,2]), removals = 0
i=1: [2,3]
Check: 2 < 2? No (no overlap, boundary touch is OK)
Keep: right = 3, removals = 0
Result: 0 removals
Kept intervals: [1,2], [2,3]
Algorithm Breakdown
Why Sort by End Time?
Sorting by end time is crucial for the greedy strategy:
- Maximize Remaining Space: Intervals with earlier end times leave more room for future intervals
- Optimal Choice: Keeping the interval with earliest end time is always optimal
- Greedy Property: This choice doesn’t prevent optimal solutions later
Counter-example (sorting by start time):
Intervals: [[1,10], [2,3], [4,5]]
Sort by start: [[1,10], [2,3], [4,5]]
Keep [1,10] → right = 10
[2,3] overlaps (2 < 10) → remove
[4,5] overlaps (4 < 10) → remove
Removals: 2
Sort by end: [[2,3], [4,5], [1,10]]
Keep [2,3] → right = 3
[4,5] no overlap (4 >= 3) → keep, right = 5
[1,10] overlaps (1 < 5) → remove
Removals: 1 (optimal!)
Overlap Detection
Two intervals [a, b] and [c, d] overlap if:
c < b (current start < previous end)
Why not c <= b?
- The problem states: “intervals that only touch at the boundary are considered non-overlapping”
- So
[1,2]and[2,3]are not overlapping - We use
<instead of<=
Greedy Strategy
The algorithm uses a greedy strategy:
- Always keep the first interval (after sorting)
- For each subsequent interval:
- If it doesn’t overlap with the last kept interval → keep it
- If it overlaps → remove it (we already have a better choice)
This maximizes the number of kept intervals, which minimizes removals.
Time & Space Complexity
- Time Complexity:
- Sorting: O(n log n) where n is the number of intervals
- Iteration: O(n) - single pass through sorted intervals
- Total: O(n log n)
- Space Complexity: O(1)
- Only using a few variables
- Sorting is in-place (or O(n) if not in-place, but typically O(1) extra space)
Key Points
- Sort by End Time: Critical for greedy strategy to work
- Greedy Choice: Keep intervals with earliest end times
- Overlap Check: Use
<not<=(boundary touch is non-overlapping) - Optimal: Greedy approach finds optimal solution
- Simple: Straightforward implementation after sorting
Common Mistakes
- Empty array:
[]→ return 0 - Single interval:
[[1,2]]→ return 0 - No overlaps:
[[1,2],[3,4],[5,6]]→ return 0 - All overlap:
[[1,3],[1,3],[1,3]]→ return 2 - Boundary touch:
[[1,2],[2,3]]→ return 0 (non-overlapping) -
Nested intervals:
[[1,5],[2,3],[4,6]]→ return 1 - Wrong sort key: Sorting by start time instead of end time
- Wrong overlap condition: Using
<=instead of< - Not handling empty input: Forgetting edge case
- Wrong initialization: Not initializing
rightcorrectly - Off-by-one error: Starting loop from wrong index
Related Problems
- 56. Merge Intervals - Merge overlapping intervals
- 252. Meeting Rooms - Check if intervals overlap
- 253. Meeting Rooms II - Count overlapping intervals
- 452. Minimum Number of Arrows to Burst Balloons - Similar greedy interval problem
- 646. Maximum Length of Pair Chain - Find longest chain of non-overlapping intervals
Tags
Array, Greedy, Sorting, Intervals, Dynamic Programming, Medium
Key Takeaways
- Define state: what subproblem does
dp[i](ordp[i][j]) represent? - Recurrence: how does the answer build from smaller indices?
- Base cases first; optimize space if only prior row/layer is needed.
References
- LC 435: Non-overlapping Intervals on LeetCode
- LeetCode Discuss — LC 435: Non-overlapping Intervals
- LeetCode Editorial (may require premium)