For most interval questions, you have two main approaches:
- Sort intervals by start time
- Sort intervals by end time
Sort Intervals by Start Time
This approach is useful for checking if intervals overlap and for merging overlapping intervals.
# Check if person can attend all their meetings.
intervals.sort(key=lambda x: x[0])
for i in range(len(intervals)-1):
cur, nxt = intervals[i], intervals[i+1]
if cur[1] > nxt[0]:
return False
return True
When you find overlapping intervals, you can merge them by updating the end time:
Merging intervals: take previous interval, set prev.end = max(prev.end, current.end)
Sort Intervals by End Time
This approach is useful for picking as many non-overlapping intervals as possible (activity selection problem).
The optimal solution is to greedily select intervals with the earliest end times: \(maxMeetings(start, end, meetings) = 1 + maxMeetings(start+meetings[0][1], end, meetings[1:])\)
It’s even easier iteratively:
def maxNonOverlap(intervals):
intervals.sort(key=lambda x: x[1]) # Sort by end time
count = 0
end = float('-inf')
for start, finish in intervals:
if start >= end: # No overlap with last selected interval
count += 1
end = finish
return count
Fun fact: “Finding the minimum number of intervals to remove for non-overlapping intervals” is equivalent to “Finding the maximum number of non-overlapping intervals you can take” - just subtract your answer from the total number of intervals.
Summary
In short, there are two archetypal questions in interval problems:
- Merging overlapping intervals - sort by start time
- Maximum number of non-overlapping intervals - sort by end time
Most interval problems are modifications or inversions of these two types!