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: [] visualization_examples: - id: "find-pair-sum" title: "Find pair with sum = 10" input: array: [1, 2, 4, 6, 8, 10] target: 10 code: | left, right = 0, len(arr) - 1 while left < right: curr = arr[left] + arr[right] if curr == target: return [left, right] elif curr < target: left += 1 else: right -= 1 steps: - id: "1" description: "Initialize pointers at both ends of the sorted array. Left starts at index 0 (value 1), right starts at index 5 (value 10)." structures: array: type: array values: - { value: 1, state: active, annotations: ["L"] } - { value: 2, state: default } - { value: 4, state: default } - { value: 6, state: default } - { value: 8, state: default } - { value: 10, state: active, annotations: ["R"] } pointers: { left: 0, right: 5 } variables: { left: 0, right: 5, target: 10 } codeHighlight: { startLine: 1, endLine: 1 } - id: "2" description: "Calculate current sum: 1 + 10 = 11. This is greater than target (10), so we need a smaller sum. Move right pointer left." structures: array: type: array values: - { value: 1, state: comparing, annotations: ["L"] } - { value: 2, state: default } - { value: 4, state: default } - { value: 6, state: default } - { value: 8, state: default } - { value: 10, state: comparing, annotations: ["R"] } pointers: { left: 0, right: 5 } variables: { left: 0, right: 5, curr: 11, target: 10 } codeHighlight: { startLine: 3, endLine: 8 } - id: "3" description: "Right pointer moved to index 4 (value 8). Now checking new sum." structures: array: type: array values: - { value: 1, state: active, annotations: ["L"] } - { value: 2, state: default } - { value: 4, state: default } - { value: 6, state: default } - { value: 8, state: active, annotations: ["R"] } - { value: 10, state: visited } pointers: { left: 0, right: 4 } variables: { left: 0, right: 4, target: 10 } codeHighlight: { startLine: 8, endLine: 8 } - id: "4" description: "Calculate current sum: 1 + 8 = 9. This is less than target (10), so we need a larger sum. Move left pointer right." structures: array: type: array values: - { value: 1, state: comparing, annotations: ["L"] } - { value: 2, state: default } - { value: 4, state: default } - { value: 6, state: default } - { value: 8, state: comparing, annotations: ["R"] } - { value: 10, state: visited } pointers: { left: 0, right: 4 } variables: { left: 0, right: 4, curr: 9, target: 10 } codeHighlight: { startLine: 3, endLine: 7 } - id: "5" description: "Left pointer moved to index 1 (value 2). Now checking new sum." structures: array: type: array values: - { value: 1, state: visited } - { value: 2, state: active, annotations: ["L"] } - { value: 4, state: default } - { value: 6, state: default } - { value: 8, state: active, annotations: ["R"] } - { value: 10, state: visited } pointers: { left: 1, right: 4 } variables: { left: 1, right: 4, target: 10 } codeHighlight: { startLine: 7, endLine: 7 } - id: "6" description: "Calculate current sum: 2 + 8 = 10. This equals the target! We found our pair at indices [1, 4]." structures: array: type: array values: - { value: 1, state: visited } - { value: 2, state: found, annotations: ["L"] } - { value: 4, state: default } - { value: 6, state: default } - { value: 8, state: found, annotations: ["R"] } - { value: 10, state: visited } pointers: { left: 1, right: 4 } variables: { left: 1, right: 4, curr: 10, target: 10 } codeHighlight: { startLine: 4, endLine: 5 }