What two pointers is
Two pointers is a technique that keeps two indices into a sequence and moves them based on what you see, so you sweep through once instead of nesting loops. The most common shape puts a left pointer on the smallest value and a right pointer on the largest, and walks them toward each other.
In this lesson you'll learn the rule that decides which pointer moves, why each move safely rules out many pairs at once, and where the pattern breaks.
Here's the problem it was born for. You have a sorted array —
[2, 3, 5, 8, 11] — and a target, 13. Which two values add up to it? The brute-force move checks every pair (2+3, 2+5, 2+8, …) — a loop inside a loop that completely ignores the sortedness. With two pointers you look at their sum, sum = arr[left] + arr[right]:- sum too small (
sum < target)? The only way up is a bigger small value → move left rightward. - sum too big (
sum > target)? The only way down is a smaller big value → move right leftward. - sum just right? Found it.
Each move rules out a whole set of pairs. The same idea powers reversing in place, squishing duplicates, and comparing a string's ends — always two indices walking toward (or with) each other.
Play it out in words: gifts priced $2, $3, $5, $8, $11 and a $16 budget for two of them. Left hand on the cheapest ($2), right hand on the priciest ($11): $2 + $11 = $13 — too little, and the only fix is a pricier cheap gift, so the left hand moves: $3 + $11 = $14, still short. Move it again: $5 + $11 = $16 — exact. Three checks instead of testing every pair. Watch the two hands below. (The coded pair-sum and its cousins live in the See it step.)
This section has a step-by-step animation in the interactive lesson.
Quick check
In sorted pair-sum, the current sum is too BIG. You move…
Answer it in the interactive lesson to keep your progress.
How it works, step by step
We have a sorted array and want two values that add up to a target like
13. Instead of testing every pair, we put one marker at each end and move them toward each other, letting the sorted order tell us which one to move.The steps
- Put
lefton the first element andrighton the last. - Look at
sum = arr[left] + arr[right]. - If
sumequals the target → found the pair. - If
sumis too small, the only way to grow it is a bigger low value → moveleftrightward. - If
sumis too big, the only way to shrink it is a smaller high value → moverightleftward. - Stop when the markers meet (
left >= right) — no pair exists.
Trace it on [2, 3, 5, 8, 11], target 13 (L=0→2, R=4→11):
2 + 11 = 13→ match, done.
And for target
10: 2 + 11 = 13 too big → R=3; 2 + 8 = 10 → match. Each step throws away pairs you never have to test.(In the See it step, exactly one marker moves after every comparison — "too small,
left moves" or "too big, right moves.")Two-Pointer Pair Sum in JavaScript
function twoSumSorted(sorted, target) {
let lo = 0, hi = sorted.length - 1; // one marker at each end
while (lo < hi) {
const sum = sorted[lo] + sorted[hi];
if (sum === target) return [lo, hi];
if (sum < target) lo++; // too small → raise the low end
else hi--; // too big → lower the high end
}
return [-1, -1]; // markers crossed → no pair
}
console.log(twoSumSorted([2, 3, 5, 8, 11], 13)); // [0, 4]
console.log(twoSumSorted([2, 3, 5, 8, 11], 10)); // [0, 3]Quick check
On [2, 3, 5, 8, 11] with target 10: after 2 + 11 = 13 (too big), predict the next comparison.
Answer it in the interactive lesson to keep your progress.
Where it breaks (and how not to get burned)
- Unsorted input. The move-left/move-right logic only works because the array is sorted. If it is not, sort first — or reach for a hash map instead.
- Moving the wrong pointer. Nudge the wrong side and you will skip the answer or loop forever. Tie each move to why: too small → raise the low end; too big → lower the high end.
- Crossing the pointers. Stop when
left >= right; reading past that double-counts or reprocesses the middle.
Quick check
Opposite-ends two pointers on UNSORTED data…
Answer it in the interactive lesson to keep your progress.
How fast is it? (complexity)
Using the vocabulary from the Complexity & Time lesson, because each pointer only marches inward and they meet in the middle, the whole array is swept in a single pass:
| Approach to pair-sum (sorted) | Time | Space | Why |
|---|---|---|---|
| brute force — every pair | O(n²) | O(1) | ignores the sorted order |
| two pointers — sweep inward | O(n) | O(1) | each step retires one end |
Every step moves a pointer inward and pointers never back up, so after at most
n moves they meet — that is the whole O(n) argument, and no extra memory is needed.Key takeaways:
- Keep two indices and move them by a rule instead of nesting loops.
- Opposite-ends needs sorted data; a slow/fast pair (dedupe, reverse) does not.
- One
O(n)sweep,O(1)space — stop when the pointers cross.
The go-to whenever the data is sorted or you're working from both ends. These classics each get their own lesson later (no need to know them yet): container with most water (move the shorter wall inward — animated in See it: always the shorter wall moves, because keeping it can never help), 3Sum (fix one value, two-pointer the rest), and remove duplicates in place (a slow write pointer and a fast read pointer, shown here). Next up in order: Sliding Window — a moving stretch of the array instead of its two ends.
Removing Duplicates in Place in JavaScript
function dedupe(sortedArr) {
if (sortedArr.length === 0) return 0;
let write = 0;
for (let read = 1; read < sortedArr.length; read++) {
if (sortedArr[read] !== sortedArr[write]) {
write++;
sortedArr[write] = sortedArr[read];
}
}
return write + 1;
}
const data = [1, 1, 2, 2, 2, 3];
const uniqueLen = dedupe(data);
console.log('unique count:', uniqueLen);
console.log('front of array:', data.slice(0, uniqueLen));