Pattern Guide
Two Pointers
"Prune half the search space at every step."
Two pointers is a prune-and-search strategy. At every step you eliminate a chunk of possibilities based on what you know. Learn the three pointer configurations and when each applies.
14 min readsliding window problems →
Problems you can solve with this pattern
10 problems · click any to start solving
Opposite-ends template (two sum on sorted array)
let left = 0, right = arr.length - 1;
while (left < right) {
const val = compute(arr[left], arr[right]);
if (val === target) return [left, right];
else if (val < target) left++; // need bigger → move left forward
else right--; // need smaller → move right back
}Two pointers is fundamentally a prune-and-search strategy. You maintain two indices and at each step you move at least one of them, eliminating solutions that can't possibly be optimal. The key insight is that the structure of the problem (sorted array, or a monotonic relationship between the pointer positions and the answer) lets you safely discard large chunks of the search space with each move.
Three Pointer Configurations
| Configuration | Start Positions | Classic Problems |
|---|---|---|
| Opposite ends | i=0, j=n-1, move toward center | Two Sum (sorted), Container With Most Water, Reverse |
| Same direction | i=0, j=0, j runs ahead | Remove duplicates, partition, slow/fast |
| Slow + fast (LL) | slow=head, fast=head | Cycle detection, middle of LL, kth from end |
When NOT to use two pointers: if the input contains negative values, adding an element can both increase AND decrease the sum. The window no longer has monotonic behavior — shrinking from the left doesn't guarantee we get closer to the target. Use prefix sum + hashmap instead.
Core Template
Opposite-ends template (two sum on sorted array)
let left = 0, right = arr.length - 1;
while (left < right) {
const val = compute(arr[left], arr[right]);
if (val === target) return [left, right];
else if (val < target) left++; // need bigger → move left forward
else right--; // need smaller → move right back
}Same-direction template (remove/filter in-place)
let slow = 0;
for (let fast = 0; fast < n; fast++) {
if (shouldKeep(arr[fast])) {
arr[slow] = arr[fast];
slow++;
}
}
return slow; // new lengthTwo pointer decision guide:
- "Find pair with sum = target in sorted array" → left/right converging pointers
- "3Sum / 4Sum" → fix k-2 elements with loops, two pointers for final pair
- "Partition array by condition" → slow/fast or lo/mid/hi (Dutch flag)
- "Container / area maximization" → greedy: move the limiting (smaller) side inward
- "Valid palindrome with one deletion" → two pointers + helper check on mismatch
- "Linked list intersection" → equalize path lengths by switching lists
While loop condition rule: access
- "Find pair with sum = target in sorted array" → left/right converging pointers
- "3Sum / 4Sum" → fix k-2 elements with loops, two pointers for final pair
- "Partition array by condition" → slow/fast or lo/mid/hi (Dutch flag)
- "Container / area maximization" → greedy: move the limiting (smaller) side inward
- "Valid palindrome with one deletion" → two pointers + helper check on mismatch
- "Linked list intersection" → equalize path lengths by switching lists
While loop condition rule: access
curr.next → while (curr). Access curr.next.next → while (curr && curr.next).