Two Pointers
Use two pointers to traverse a sorted array or string from both ends (or at different speeds) to solve problems in O(n) time with O(1) space.
Learn & Reference
Understanding the Pattern
Two Pointers Pattern
Two pointers is one of the most versatile patterns in algorithm interviews. The idea is simple: instead of using nested loops (O(n²)), use two index variables that move through the data in a coordinated way to solve the problem in a single pass.
Core Concept
Place two pointers at strategic positions and move them based on a condition. The key insight is that pointer movement eliminates candidates — each step rules out an entire group of possibilities.
The 3 Two Pointer Archetypes
1. Opposite Ends (Converging)
Start one pointer at the beginning, one at the end. Move inward based on a condition. Works on sorted arrays and palindromes.
Why it works: In a sorted array, if the sum is too small, moving the left pointer right increases it. If too large, moving the right pointer left decreases it. Each step eliminates an entire row/column of the search space.
2. Same Direction (Fast & Slow)
Both pointers start at the beginning. The fast pointer explores ahead while the slow pointer marks a position. Used for removing duplicates in-place, partitioning, and cycle detection.
Why it works: The slow pointer marks "where to write next" while the fast pointer scans for the next valid element.
3. Two Sequences
One pointer per sequence. Advance whichever pointer points to the smaller element. Used for merging sorted arrays and intersection problems.
When Two Pointers Fails
- When the array is not sorted (and sorting would lose required info like indices)
- When you need to find ALL pairs, not just one (may need hash map instead)
- When the problem requires non-contiguous elements
- When O(1) space isn't possible (need auxiliary storage)
Common Mistakes
- Forgetting to sort first: Two-pointer convergence only works on sorted data
- Off-by-one on boundaries: Use < vs <= carefully with while (left < right)
- Skipping duplicates incorrectly: When the problem says "unique triplets", skip duplicate values AFTER finding a match
- Modifying array when indices matter: If you need original indices, use a hash map instead
- Not handling empty or single-element arrays: Always check edge cases
When to Use
Template
1# --- Opposite Ends (Pair Sum) ---2def pair_with_sum(nums, target):3"""nums must be sorted"""4left, right = 0, len(nums) - 15while left < right:6total = nums[left] + nums[right]7if total == target:8return [left, right]9elif total < target:10left += 111else:12right -= 113return []1415# --- Same Direction (Remove In-Place) ---16def remove_element(nums, val):17"""Remove all occurrences of val, return new length"""18slow = 019for fast in range(len(nums)):20if nums[fast] != val:21nums[slow] = nums[fast]22slow += 123return slow2425# --- Opposite Ends (Palindrome) ---26def is_palindrome(s):27left, right = 0, len(s) - 128while left < right:29if s[left] != s[right]:30return False31left += 1; right -= 132return True
Syntax Reference
1# --- Opposite Ends ---2left, right = 0, len(arr) - 13while left < right:4if condition(arr[left], arr[right]):5# found answer or process6left += 1 # or right -= 17elif need_bigger:8left += 19else:10right -= 11112# --- Same Direction (fast/slow) ---13slow = 014for fast in range(len(arr)):15if arr[fast] != val_to_skip:16arr[slow] = arr[fast]17slow += 118# slow = new length1920# --- Two Sequences ---21i, j = 0, 022while i < len(a) and j < len(b):23if a[i] <= b[j]:24process(a[i]); i += 125else:26process(b[j]); j += 1
Common Recipes
1# --- Two Sum (sorted array) ---2def two_sum_sorted(nums, target):3left, right = 0, len(nums) - 14while left < right:5total = nums[left] + nums[right]6if total == target:7return [left, right]8elif total < target:9left += 110else:11right -= 112return []1314# --- Three Sum ---15def three_sum(nums):16nums.sort()17result = []18for i in range(len(nums) - 2):19if i > 0 and nums[i] == nums[i-1]:20continue # skip duplicates21left, right = i + 1, len(nums) - 122while left < right:23total = nums[i] + nums[left] + nums[right]24if total == 0:25result.append([nums[i], nums[left], nums[right]])26while left < right and nums[left] == nums[left+1]:27left += 1 # skip duplicates28while left < right and nums[right] == nums[right-1]:29right -= 130left += 1; right -= 131elif total < 0:32left += 133else:34right -= 135return result3637# --- Is Palindrome ---38def is_palindrome(s):39left, right = 0, len(s) - 140while left < right:41if s[left] != s[right]:42return False43left += 1; right -= 144return True4546# --- Remove Duplicates In-Place ---47def remove_duplicates(nums):48if not nums:49return 050slow = 051for fast in range(1, len(nums)):52if nums[fast] != nums[slow]:53slow += 154nums[slow] = nums[fast]55return slow + 15657# --- Container With Most Water ---58def max_area(height):59left, right = 0, len(height) - 160best = 061while left < right:62area = min(height[left], height[right]) * (right - left)63best = max(best, area)64if height[left] < height[right]:65left += 166else:67right -= 168return best
Complexity
| Operation | Time | Space |
|---|---|---|
| Pair sum (sorted) | O(n) | O(1) |
| Three sum | O(n²) | O(1) |
| Remove duplicates | O(n) | O(1) |
| Merge sorted | O(n+m) | O(n+m) |
| Palindrome check | O(n) | O(1) |
Watch Out
- ✗When the array is not sorted (and sorting would lose required info like indices)
- ✗When you need to find ALL pairs, not just one (may need hash map instead)
- ✗When the problem requires non-contiguous elements
- ✗When O(1) space isn't possible (need auxiliary storage)
- ✗Forgetting to sort first: Two-pointer convergence only works on sorted data
- ✗Off-by-one on boundaries: Use < vs <= carefully with while (left < right)
- ✗Skipping duplicates incorrectly: When the problem says "unique triplets", skip duplicate values AFTER finding a match
- ✗Modifying array when indices matter: If you need original indices, use a hash map instead
- ✗Not handling empty or single-element arrays: Always check edge cases