Two Pointers
Two pointers is one of those techniques that feels like magic the first time you see it. Instead of nested loops giving you O(n²), you walk two pointers through the array and solve it in O(n). Beautiful.
The Core Idea
Use two indices (pointers) that move through your data structure. How they move depends on the problem - sometimes they start at opposite ends and walk toward each other, sometimes they start together and one chases the other.
Two pointers work best on sorted arrays or when you're looking for pairs/subarrays with a specific property. The sorted order gives you information about how to move the pointers.
The Two Main Patterns
1. Opposite Ends (Converging)
Start one pointer at the beginning, one at the end, and move them toward each other:
left = 0
right = len(arr) - 1
while left < right:
# Check condition, move pointers
if some_condition:
left += 1
else:
right -= 1
2. Same Direction (Fast & Slow)
Both pointers start at the beginning, but one moves faster:
slow = 0
for fast in range(len(arr)):
if some_condition:
arr[slow] = arr[fast]
slow += 1
Real Example: Valid Palindrome
Check if a string reads the same forwards and backwards. Classic two-pointer territory:
def isPalindrome(s):
left, right = 0, len(s) - 1
while left < right:
# Skip non-alphanumeric
while left < right and not s[left].isalnum():
left += 1
while left < right and not s[right].isalnum():
right -= 1
if s[left].lower() != s[right].lower():
return False
left += 1
right -= 1
return True
Real Example: Two Sum II (Sorted Array)
When the array is sorted, two pointers beats a hash map in space efficiency:
def twoSum(numbers, target):
left, right = 0, len(numbers) - 1
while left < right:
total = numbers[left] + numbers[right]
if total == target:
return [left + 1, right + 1]
elif total < target:
left += 1 # Need bigger sum
else:
right -= 1 # Need smaller sum
The sorted order tells us: if sum is too small, move left up (bigger number). If too big, move right down (smaller number).
When to Use Two Pointers
- Sorted arrays - Finding pairs with a target sum
- Palindrome checks - Compare from both ends
- Removing duplicates - In-place with slow/fast pointers
- Merging sorted arrays - Compare heads of both
- Container with most water - Classic converging pointers
With sorted data, each pointer movement eliminates possibilities. Moving left up means "all pairs with current left are too small." That's a lot of eliminated options in one step!
Common Mistakes
- Forgetting to sort - Two pointers on unsorted data often doesn't work
- Off-by-one errors - Use
<vs<=carefully - Infinite loops - Make sure pointers always move