feat(patterns): tutorial system
This commit is contained in:
166
backend/data/patterns/two-pointers.yaml
Normal file
166
backend/data/patterns/two-pointers.yaml
Normal file
@@ -0,0 +1,166 @@
|
||||
name: Two Pointers
|
||||
slug: two-pointers
|
||||
difficulty_level: 2
|
||||
|
||||
description: >
|
||||
Use two pointers to traverse data from different positions, often moving
|
||||
toward or away from each other. This technique transforms O(n²) brute force
|
||||
into O(n) by eliminating redundant comparisons.
|
||||
|
||||
when_to_use: |
|
||||
- Sorted arrays where you need to find pairs
|
||||
- Linked list cycle detection
|
||||
- Removing duplicates in-place
|
||||
- Partitioning arrays
|
||||
- Palindrome checking
|
||||
|
||||
metaphor: |
|
||||
Imagine two people reading a book from opposite ends, each moving toward the
|
||||
middle. The person at the back skips ahead when they find what they're looking
|
||||
for, while the person at the front moves forward when they don't match. They
|
||||
meet somewhere in the middle, having searched the entire book without either
|
||||
person reading the same page twice.
|
||||
|
||||
Another way to think about it: squeezing toothpaste from both ends of the
|
||||
tube. You apply pressure from each side, working toward the center until
|
||||
you've gotten everything out.
|
||||
|
||||
core_concept: |
|
||||
The **two pointers** technique eliminates the need for nested loops by
|
||||
maintaining two positions that move through the data based on conditions.
|
||||
|
||||
The key insight is that when data has *structure* (like being sorted), you
|
||||
can make intelligent decisions about which pointer to move. If the current
|
||||
pair is too small, moving the left pointer right increases the sum. If it's
|
||||
too large, moving the right pointer left decreases it.
|
||||
|
||||
This reduces O(n²) brute force (checking all pairs) to O(n) because each
|
||||
element is visited at most twice—once by each pointer.
|
||||
|
||||
visualization: |
|
||||
**Example: Find pair with sum = 10 in sorted array**
|
||||
|
||||
```
|
||||
Array: [1, 2, 4, 6, 8, 10] Target: 10
|
||||
L R
|
||||
|
||||
Step 1: 1 + 10 = 11 > 10 → Sum too large, move R left
|
||||
L R
|
||||
|
||||
Step 2: 1 + 8 = 9 < 10 → Sum too small, move L right
|
||||
L R
|
||||
|
||||
Step 3: 2 + 8 = 10 ✓ → Found! Return [1, 4]
|
||||
```
|
||||
|
||||
**Key insight**: Because the array is sorted, we know exactly which pointer
|
||||
to move. Too big? Decrease the larger value. Too small? Increase the smaller.
|
||||
|
||||
code_template: |
|
||||
def two_pointers(arr: list, target: int) -> list:
|
||||
"""Two pointers converging from opposite ends."""
|
||||
left, right = 0, len(arr) - 1
|
||||
|
||||
while left < right:
|
||||
current = arr[left] + arr[right]
|
||||
|
||||
if current == target:
|
||||
return [left, right] # Found!
|
||||
elif current < target:
|
||||
left += 1 # Need larger sum
|
||||
else:
|
||||
right -= 1 # Need smaller sum
|
||||
|
||||
return [] # No solution found
|
||||
|
||||
|
||||
def two_pointers_same_direction(arr: list) -> int:
|
||||
"""Two pointers moving in same direction (slow/fast)."""
|
||||
slow = 0
|
||||
|
||||
for fast in range(len(arr)):
|
||||
if some_condition(arr[fast]):
|
||||
arr[slow] = arr[fast]
|
||||
slow += 1
|
||||
|
||||
return slow # New length
|
||||
|
||||
recognition_signals:
|
||||
- "sorted array"
|
||||
- "find pair with sum"
|
||||
- "two sum"
|
||||
- "in-place modification"
|
||||
- "remove duplicates"
|
||||
- "partition array"
|
||||
- "palindrome"
|
||||
- "container with most water"
|
||||
- "trapping rain water"
|
||||
- "move zeros"
|
||||
|
||||
common_mistakes:
|
||||
- title: Off-by-one with boundaries
|
||||
description: |
|
||||
Using `<=` instead of `<` when pointers should not overlap causes
|
||||
infinite loops or double-counting elements.
|
||||
fix: |
|
||||
For converging pointers, use `while left < right`. Only use `<=` when
|
||||
the same element can be part of the answer twice.
|
||||
|
||||
- title: Not handling duplicates
|
||||
description: |
|
||||
When the problem asks for unique pairs, forgetting to skip duplicate
|
||||
values leads to repeated answers.
|
||||
fix: |
|
||||
After finding a match, skip over duplicates:
|
||||
```python
|
||||
while left < right and arr[left] == arr[left + 1]:
|
||||
left += 1
|
||||
```
|
||||
|
||||
- title: Moving both pointers at once
|
||||
description: |
|
||||
Moving both pointers simultaneously after finding a match can skip
|
||||
valid solutions.
|
||||
fix: |
|
||||
Move one pointer at a time and let the next iteration decide the other.
|
||||
After a match, move both only when you've recorded the result.
|
||||
|
||||
- title: Forgetting the sorted requirement
|
||||
description: |
|
||||
Two pointers only works predictably on sorted data. Applying it to
|
||||
unsorted arrays gives wrong results.
|
||||
fix: |
|
||||
Sort first if needed (adds O(n log n)), or use a hash map approach
|
||||
instead if sorting changes the problem semantics.
|
||||
|
||||
variations:
|
||||
- name: Opposite-direction (converging)
|
||||
description: |
|
||||
Pointers start at opposite ends and move toward each other. Used for
|
||||
pair problems in sorted arrays.
|
||||
example: "Two Sum II, Container With Most Water, Valid Palindrome"
|
||||
|
||||
- name: Same-direction (fast-slow)
|
||||
description: |
|
||||
Both pointers start at the same end but move at different speeds or
|
||||
based on different conditions. Used for in-place modifications.
|
||||
example: "Remove Duplicates, Move Zeros, Remove Element"
|
||||
|
||||
- name: Sliding window variant
|
||||
description: |
|
||||
Two pointers defining a window that expands and contracts. Technically
|
||||
a separate pattern but uses similar mechanics.
|
||||
example: "Minimum Window Substring, Longest Substring Without Repeating"
|
||||
|
||||
- name: Three pointers
|
||||
description: |
|
||||
Extension with three pointers for problems involving triplets or
|
||||
partitioning into three sections.
|
||||
example: "3Sum, Sort Colors (Dutch National Flag)"
|
||||
|
||||
related_patterns:
|
||||
- sliding-window
|
||||
- fast-slow-pointers
|
||||
- binary-search
|
||||
|
||||
prerequisite_patterns: []
|
||||
Reference in New Issue
Block a user