Two-pointer technique — when do you reach for it
Use two pointers when scanning a sorted array or linked list and a nested loop would be O(n²). Variants: opposite ends converging (sorted two-sum, palindrome, container with most water), and fast/slow (cycle detection, middle of list). Turns O(n²) into O(n) with O(1) space.
The two-pointer technique uses two index/node references moving through a structure, usually turning a brute-force O(n²) nested loop into O(n) with O(1) extra space.
When to reach for it — the signals
- The input is sorted (or you can sort it), or it's a linked list.
- The brute-force solution is a nested loop comparing pairs.
- You're looking for a pair / triplet / subarray that satisfies a condition.
- You need O(1) space (can't use a hash set).
Variant 1: opposite ends converging
Two pointers start at the start and end, move toward each other based on a comparison. Works because the array is sorted — you can reason about which pointer to move.
Sorted two-sum:
function twoSum(sorted, target) {
let lo = 0, hi = sorted.length - 1;
while (lo < hi) {
const sum = sorted[lo] + sorted[hi];
if (sum === target) return [lo, hi];
if (sum < target) lo++; // need bigger → move left pointer right
else hi--; // need smaller → move right pointer left
}
return null;
}Also: palindrome check, container with most water, 3-sum (fix one, two-pointer the rest), reversing in place.
Variant 2: fast & slow pointers
Two pointers moving at different speeds through the same structure — classic for linked lists:
- Cycle detection (Floyd's algorithm) — fast moves 2, slow moves 1; if they meet, there's a cycle.
- Find the middle — when fast reaches the end, slow is at the middle.
- Nth from the end — offset the pointers by N.
Variant 3: same-direction (sliding-window-adjacent)
Two pointers moving the same direction at different rates — e.g. removing duplicates from a sorted array in place (a "write" pointer and a "read" pointer), or partitioning. (The sliding window is a close cousin.)
Why it works / the catch
The whole technique depends on being able to eliminate possibilities by moving a pointer — which usually requires sorted data (so a comparison tells you which direction is fruitless) or the linked-list structure (for fast/slow). On unsorted data with no such property, two pointers don't apply — you'd use a hash map instead (e.g. unsorted two-sum is O(n) with a hash set, not two pointers).
The framing
"I reach for two pointers when I've got a sorted array or a linked list and the brute force is an O(n²) nested loop over pairs — it usually gets me to O(n) with O(1) space. The main variants: opposite ends converging — sorted two-sum, palindrome, container with most water — where sortedness lets me reason about which pointer to move; and fast/slow pointers for linked lists — cycle detection, finding the middle. The key requirement is that I can eliminate options by moving a pointer, which is why it needs sorted data or list structure — on unsorted data I'd reach for a hash map instead."
Follow-up questions
- •Why does the opposite-ends variant require a sorted array?
- •How does Floyd's cycle detection work?
- •When would you use a hash map instead of two pointers?
- •How does the sliding window relate to two pointers?
Common mistakes
- •Applying opposite-ends two pointers to unsorted data.
- •Off-by-one errors in the pointer bounds / loop condition.
- •Infinite loops from not always advancing a pointer.
- •Reaching for two pointers when a hash map is the better tool.
Performance considerations
- •Turns O(n²) into O(n) time with O(1) extra space — the space win matters when a hash-map solution would be O(n) space. If sorting is needed first, it's O(n log n) overall.
Edge cases
- •Empty input or a single element.
- •Pointers crossing — the loop condition (lo < hi).
- •Duplicates (e.g. in 3-sum, skip them).
- •Even vs odd length when finding the middle.
Real-world examples
- •Classic interview problems: two-sum (sorted), 3-sum, valid palindrome, linked list cycle detection.
- •In-place array partitioning and dedup.