Two Pointers
Two pointers is not one algorithm - it is a pattern that turns many O(n²) nested-loop problems into a single O(n) walk. The idea: keep two positions in the array and move them toward each other (or one chasing the other), using what you know about the data - usually that it is sorted - to rule out possibilities without ever checking them. It is the most common trick behind "how is this suddenly linear?" solutions.
Watch the pointers close in
Press play - or drag the timeline and step through it yourself.Sorted array. Which two values add up to 49? Start one pointer at each end.
Complexity
Two people searching a bookshelf
Two friends look for two books whose page counts add up to exactly 1,000, on a shelf sorted from thinnest to thickest. One starts at the thin end, the other at the thick end. They compare: 200 + 950 = 1,150 - too much, so the thick-end friend steps left (that thick book can never work - even the thinnest partner overshoots). 200 + 700 = 900 - too little, thin-end friend steps right. Each comparison permanently removes one book from the game. They meet in the middle having checked each book once.
How it works, step by step
Sort the array (or receive it sorted - that is the usual precondition).
Put a left pointer at the start and a right pointer at the end.
Look at the sum of the two pointed values.
Sum too small? The left value is useless - even paired with the biggest value it fails. Move the left pointer right.
Sum too big? The right value is useless by the same logic. Move the right pointer left.
Every step eliminates one value forever, so after at most n steps you either find the pair or the pointers meet.
The code, in JavaScript
The classic: two values in a sorted array that add to a target. One loop, no nesting - the sort order is what lets each step discard a value safely.
function pairWithSum(sorted, target) {
let l = 0;
let r = sorted.length - 1;
while (l < r) {
const sum = sorted[l] + sorted[r];
if (sum === target) return [l, r]; // found the pair
if (sum < target) l++; // left value can never work
else r--; // right value can never work
}
return null; // no such pair
}
pairWithSum([1, 3, 4, 6, 8, 11], 14); // → [1, 4] (3 + 11)
pairWithSum([1, 3, 4, 6, 8, 11], 20); // → nullDry run: finding a pair that sums to 14 in [1, 3, 4, 6, 8, 11]
The same steps the visualizer plays, written as a table. Generated by running the real algorithm - not written by hand.
| Step | Step | Array | What happened |
|---|---|---|---|
| 1 | 1 | [1, 3, 4, 6, 8, 11] | 1 + 11 = 12 > 11. Even with the smallest partner, 11 is too big - rule it out, move right pointer left. |
| 2 | 2 | [1, 3, 4, 6, 8, 11] | 1 + 8 = 9 < 11. Even with the biggest partner, 1 is too small - rule it out, move left pointer right. |
| 3 | 3 | [1, 3, 4, 6, 8, 11] | 3 + 8 = 11 - exactly the target! |
Good choice when…
- The array is sorted and the question involves pairs, sums, or differences - the order is what makes discarding safe.
- You need to rearrange or filter in place - the fast/slow shape does it with O(1) extra memory.
- You are comparing a sequence with its own reverse (palindromes) or merging two sorted sequences.
- A nested loop solution exists and each step of it could obviously "skip ahead" - that instinct usually means two pointers applies.
Bad choice when…
- The data is unsorted and sorting is too expensive or forbidden - for pair-sum on unsorted data, a hash map is O(n) without sorting.
- The decision at each step cannot rule anything out - two pointers only works when moving a pointer is provably safe.
- You need all pairs, not one - reporting every pair is O(n²) output no matter the technique.
Common mistakes
- The pattern is only correct if moving a pointer never skips a valid answer. That proof comes from the sorted order - apply the same code to an unsorted array and it silently returns wrong answers.
- `while (l < r)` versus `l <= r`: for pairs you want strict `<` (a value cannot pair with itself); for palindromes either works. Copying the wrong condition between problems is a classic slip.
- In the fast/slow shape, increment `slow` before writing, or you overwrite the last unique value. Off-by-one here corrupts the array instead of throwing.
- Two pointers and sliding window are cousins, not the same: pointers moving toward each other solve pair problems; a window (both moving forward) tracks a running property of a range. Knowing which one a problem needs is half the interview.
Two Pointers vs. its closest relatives
| Algorithm | Best | Average | Worst | Space | Stable |
|---|---|---|---|---|---|
| Two Pointersthis page | O(n) | O(n) | O(n) | O(1) | - |
| Sliding Window | O(n) | O(n) | O(n) | O(k) | - |
| Binary Search | O(1) | O(log n) | O(log n) | O(1) | - |
| Linear Search | O(1) | O(n) | O(n) | O(1) | - |