feat(patterns): pointer/array tutorials
This commit is contained in:
275
backend/data/patterns/intervals.yaml
Normal file
275
backend/data/patterns/intervals.yaml
Normal file
@@ -0,0 +1,275 @@
|
||||
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: []
|
||||
Reference in New Issue
Block a user