name: Overlapping Intervals slug: intervals difficulty_level: 2 description: > Process and manipulate intervals (ranges) that may share common regions. The key insight is that sorting intervals by start time allows efficient detection and handling of overlaps through a single linear pass. when_to_use: | - Merging overlapping intervals - Inserting an interval into a sorted list - Finding gaps between intervals - Meeting room scheduling - Finding interval intersections metaphor: | Imagine scheduling meeting rooms. Each meeting is an interval of time. When two meetings overlap, you need either two rooms or to merge them into one longer booking. By sorting meetings by start time, you can easily spot overlaps—if the next meeting starts before the current one ends, they overlap. Another analogy: merging overlapping highlighter marks on a page. Sort them left to right, and if one mark starts before the previous ends, combine them into one continuous highlight. core_concept: | The **interval pattern** relies on sorting intervals by start time. Once sorted, overlapping detection becomes simple: **Two intervals [a, b] and [c, d] overlap if c <= b** (assuming a <= c after sorting) Key operations: 1. **Merge**: Extend the current interval's end to include overlapping intervals 2. **Insert**: Find where the new interval overlaps and merge as needed 3. **Count overlaps**: Track how many intervals are "active" at any point For problems needing simultaneous tracking (like minimum meeting rooms), use a **sweep line** approach: sort all start and end points together, then sweep through counting active intervals. visualization: | **Merging overlapping intervals:** ``` Input: [[1,3], [2,6], [8,10], [15,18]] (sorted by start) Process [1,3]: result = [[1,3]] Process [2,6]: 2 <= 3? Yes, overlap! Merge: [1, max(3,6)] = [1,6] result = [[1,6]] Process [8,10]: 8 <= 6? No, no overlap result = [[1,6], [8,10]] Process [15,18]: 15 <= 10? No, no overlap result = [[1,6], [8,10], [15,18]] ``` **Visualized on number line:** ``` Before: [1---3] [2------6] [8--10] [15--18] After: [1--------6] [8--10] [15--18] ``` **Meeting rooms (sweep line):** ``` Meetings: [[0,30], [5,10], [15,20]] Events (sorted): time=0: +1 (start) active=1 time=5: +1 (start) active=2 ← max time=10: -1 (end) active=1 time=15: +1 (start) active=2 ← max time=20: -1 (end) active=1 time=30: -1 (end) active=0 Max concurrent = 2 → need 2 meeting rooms ``` code_template: | def merge_intervals(intervals: list[list[int]]) -> list[list[int]]: """Merge overlapping intervals.""" if not intervals: return [] # Sort by start time intervals.sort(key=lambda x: x[0]) result = [intervals[0]] for start, end in intervals[1:]: last_end = result[-1][1] if start <= last_end: # Overlap result[-1][1] = max(last_end, end) # Extend else: result.append([start, end]) return result def insert_interval(intervals: list[list[int]], new: list[int]) -> list[list[int]]: """Insert and merge a new interval into sorted list.""" result = [] i = 0 n = len(intervals) # Add all intervals before new interval while i < n and intervals[i][1] < new[0]: result.append(intervals[i]) i += 1 # Merge overlapping intervals with new while i < n and intervals[i][0] <= new[1]: new[0] = min(new[0], intervals[i][0]) new[1] = max(new[1], intervals[i][1]) i += 1 result.append(new) # Add remaining intervals while i < n: result.append(intervals[i]) i += 1 return result def min_meeting_rooms(intervals: list[list[int]]) -> int: """Find minimum meeting rooms needed (sweep line).""" events = [] for start, end in intervals: events.append((start, 1)) # +1 for start events.append((end, -1)) # -1 for end # Sort by time, with ends before starts at same time events.sort(key=lambda x: (x[0], x[1])) max_rooms = 0 current_rooms = 0 for _, delta in events: current_rooms += delta max_rooms = max(max_rooms, current_rooms) return max_rooms def interval_intersection(A: list[list[int]], B: list[list[int]]) -> list[list[int]]: """Find intersection of two sorted interval lists.""" result = [] i = j = 0 while i < len(A) and j < len(B): # Find overlap start = max(A[i][0], B[j][0]) end = min(A[i][1], B[j][1]) if start <= end: result.append([start, end]) # Advance the interval that ends first if A[i][1] < B[j][1]: i += 1 else: j += 1 return result def can_attend_all(intervals: list[list[int]]) -> bool: """Check if a person can attend all meetings.""" intervals.sort(key=lambda x: x[0]) for i in range(1, len(intervals)): if intervals[i][0] < intervals[i-1][1]: return False # Overlap found return True recognition_signals: - "intervals" - "merge" - "overlapping" - "meeting rooms" - "schedule" - "time slots" - "range" - "insert interval" - "non-overlapping" - "intersection" common_mistakes: - title: Not sorting first description: | Trying to process unsorted intervals leads to incorrect results because overlaps aren't detected properly. fix: | Always sort intervals by start time before processing: ```python intervals.sort(key=lambda x: x[0]) ``` - title: Wrong overlap condition description: | Using `start < last_end` instead of `start <= last_end` misses adjacent intervals that should be merged (like [1,2] and [2,3]). fix: | Use `<=` for touching intervals, `<` for strict overlap only. Check problem requirements for whether touching counts as overlapping. - title: Not updating end correctly when merging description: | Setting `end = new_end` instead of `end = max(old_end, new_end)` fails when a smaller interval is contained within a larger one. fix: | Always take the maximum: ```python result[-1][1] = max(result[-1][1], end) ``` - title: Off-by-one with closed vs open intervals description: | Confusion about whether interval endpoints are inclusive `[a, b]` or exclusive `[a, b)` causes incorrect overlap detection. fix: | Clarify the convention from the problem. Most problems use closed intervals where both endpoints are included. variations: - name: Merge intervals description: | Combine all overlapping intervals into non-overlapping intervals. example: "Merge Intervals" - name: Insert interval description: | Insert a new interval into a sorted list, merging with any overlapping intervals. example: "Insert Interval" - name: Meeting rooms description: | Find minimum resources needed for concurrent intervals using sweep line or min-heap. example: "Meeting Rooms II, Car Pooling" - name: Interval intersection description: | Find common regions between two lists of intervals using two pointers. example: "Interval List Intersections" - name: Non-overlapping intervals description: | Find minimum removals to make all intervals non-overlapping. Greedy approach: keep intervals that end earliest. example: "Non-overlapping Intervals, Erase Overlap Intervals" related_patterns: - greedy - two-pointers prerequisite_patterns: []