Two Pointers
The Two Pointers pattern transforms
Real-World Analogy
Imagine two people reading the same book to check if it reads the same forwards and backwards (a palindrome):
- One person starts at the first page, another at the last
- They each read their current page and compare
- If pages match, both move one page toward the middle
- If they meet in the middle without mismatches, it is a palindrome
This is exactly how the two pointers technique works - two positions moving through data based on comparisons, converging toward a solution.
Visual Explanation
Here is how two pointers find a pair summing to 9 in a sorted array:
flowchart LR
subgraph Step1["Step 1: Sum = 1+8 = 9"]
A1["[1]"] --> B1["2"] --> C1["3"] --> D1["5"] --> E1["[8]"]
end
subgraph Step2["Found: indices 0 and 4"]
A2["1 + 8 = 9"]
end
style A1 fill:#c8e6c9
style E1 fill:#c8e6c9
The pointers start at opposite ends. Since 1 + 8 = 9 equals our target, we found the answer immediately.
Movement Logic
flowchart TD
Start[Initialize left=0, right=n-1] --> Compare{sum vs target?}
Compare -->|sum == target| Found[Return indices]
Compare -->|sum < target| MoveLeft[left++]
Compare -->|sum > target| MoveRight[right--]
MoveLeft --> Check{left < right?}
MoveRight --> Check
Check -->|Yes| Compare
Check -->|No| NotFound[No solution]
When to Use This Pattern
Use Two Pointers when you see:
- Input array is sorted or can be sorted
- Need to find pairs with a specific sum or difference
- Problem asks for in-place array modification
- Checking palindromes or string symmetry
- Comparing elements from both ends
- Merging two sorted arrays
- Removing duplicates from sorted data
Core Implementation
// Opposite Direction: Find pair with target sum
function twoSum(arr, target) {
let left = 0;
let right = arr.length - 1;
while (left < right) {
const sum = arr[left] + arr[right];
if (sum === target) {
return [left, right];
} else if (sum < target) {
left++;
} else {
right--;
}
}
return [-1, -1];
}
// Same Direction: Remove duplicates
function removeDuplicates(nums) {
if (nums.length === 0) return 0;
let slow = 0;
for (let fast = 1; fast < nums.length; fast++) {
if (nums[fast] !== nums[slow]) {
slow++;
nums[slow] = nums[fast];
}
}
return slow + 1;
}# Opposite Direction: Find pair with target sum
def two_sum(arr: list, target: int) -> list:
left, right = 0, len(arr) - 1
while left < right:
total = arr[left] + arr[right]
if total == target:
return [left, right]
elif total < target:
left += 1
else:
right -= 1
return [-1, -1]
# Same Direction: Remove duplicates
def remove_duplicates(nums: list) -> int:
if not nums:
return 0
slow = 0
for fast in range(1, len(nums)):
if nums[fast] != nums[slow]:
slow += 1
nums[slow] = nums[fast]
return slow + 1// Opposite Direction: Find pair with target sum
public int[] twoSum(int[] arr, int target) {
int left = 0, right = arr.length - 1;
while (left < right) {
int sum = arr[left] + arr[right];
if (sum == target) {
return new int[]{left, right};
} else if (sum < target) {
left++;
} else {
right--;
}
}
return new int[]{-1, -1};
}
// Same Direction: Remove duplicates
public int removeDuplicates(int[] nums) {
if (nums.length == 0) return 0;
int slow = 0;
for (int fast = 1; fast < nums.length; fast++) {
if (nums[fast] != nums[slow]) {
slow++;
nums[slow] = nums[fast];
}
}
return slow + 1;
}// Opposite Direction: Find pair with target sum
vector<int> twoSum(vector<int>& arr, int target) {
int left = 0, right = arr.size() - 1;
while (left < right) {
int sum = arr[left] + arr[right];
if (sum == target) {
return {left, right};
} else if (sum < target) {
left++;
} else {
right--;
}
}
return {-1, -1};
}
// Same Direction: Remove duplicates
int removeDuplicates(vector<int>& nums) {
if (nums.empty()) return 0;
int slow = 0;
for (int fast = 1; fast < nums.size(); fast++) {
if (nums[fast] != nums[slow]) {
slow++;
nums[slow] = nums[fast];
}
}
return slow + 1;
}// Opposite Direction: Find pair with target sum
func twoSum(arr []int, target int) []int {
left, right := 0, len(arr)-1
for left < right {
sum := arr[left] + arr[right]
if sum == target {
return []int{left, right}
} else if sum < target {
left++
} else {
right--
}
}
return []int{-1, -1}
}
// Same Direction: Remove duplicates
func removeDuplicates(nums []int) int {
if len(nums) == 0 { return 0 }
slow := 0
for fast := 1; fast < len(nums); fast++ {
if nums[fast] != nums[slow] {
slow++
nums[slow] = nums[fast]
}
}
return slow + 1
}# Opposite Direction: Find pair with target sum
def two_sum(arr, target)
left, right = 0, arr.length - 1
while left < right
sum = arr[left] + arr[right]
if sum == target
return [left, right]
elsif sum < target
left += 1
else
right -= 1
end
end
[-1, -1]
end
# Same Direction: Remove duplicates
def remove_duplicates(nums)
return 0 if nums.empty?
slow = 0
(1...nums.length).each do |fast|
if nums[fast] != nums[slow]
slow += 1
nums[slow] = nums[fast]
end
end
slow + 1
endComplexity Analysis
| Variation | Time | Space | Use Case |
|---|---|---|---|
| Opposite Direction | O(N) | O(1) | Sum pairs, palindromes |
| Same Direction | O(N) | O(1) | Remove duplicates |
| Three Pointers | O(N^2) | O(1) | 3Sum problems |
Why O(N)? Each pointer moves at most N times, and they never move backwards once they have advanced. The total work is proportional to the array length.
Common Mistakes
- Infinite loops: Forgetting to move pointers when condition is met
- Off-by-one errors: Using
<=instead of<in while condition - Not handling duplicates: Missing skip logic for duplicate values in 3Sum
- Wrong pointer movement: Moving the wrong pointer based on comparison
Next Steps
Ready to implement? Check out our Code Templates for reusable patterns.
Want to practice? Head to Practice Problems for curated LeetCode problems.