Direct answer: use the Two Pointers pattern when two positions can move through ordered data while each move rules out a meaningful set of candidates. The classic case is finding a target pair in a sorted array: place one pointer at each end, compare their sum with the target, and move the only pointer whose move can improve the sum. That turns an all-pairs search from O(n2) into one O(n) scan. The same idea also powers palindrome checks, in-place compaction, merging sorted arrays, and the inner loop of 3Sum.
Two pointers are not a magic keyword match. Start by naming what each pointer means, what portion of the input has been eliminated, and why a pointer movement cannot discard the answer. That small proof is what makes the pattern reliable in a LeetCode round or a coding interview.
Pattern Recognition Signals and Anti-Signals
| Recognition signal | Why it suggests pointers | Typical example |
|---|---|---|
| Sorted or monotonic input | Moving left or right changes a comparison predictably. | Two Sum II, 3Sum after sorting |
| A pair, triplet, or two ends must satisfy a rule | Two indices describe the candidate relationship directly. | Valid Palindrome, Container With Most Water |
| Modify a sorted array in place | A read pointer explores while a write pointer preserves the accepted prefix. | Remove Duplicates from Sorted Array |
| Merge or compare two ordered sequences | Each pointer tracks the next unconsumed value. | Merge Sorted Array, interval intersection |
| Find a contiguous region with a changing boundary | It may be the specialized same-direction form called a sliding window. | Longest substring without repeats |
Anti-signals matter too. An unsorted “find two values” problem that must preserve original indices often wants a hash map, not sorting plus pointers. A problem about any subset rather than adjacent or ordered positions may need backtracking, dynamic programming, or bitmasking. If values can be chosen repeatedly and no ordering gives a safe discard rule, do not force two pointers. For contiguous-subarray constraints, compare this guide with the Sliding Window guide; for repeated-choice optimization, consider Dynamic Programming.
The Core Invariant: What Stays True?
For the sorted target-pair version, maintain left < right and the invariant: every pair using an index left of left or right of right has already been proved unable to reach the target. Suppose nums[left] + nums[right] is too small. Because the array is sorted, pairing nums[left] with any element at or before right is no larger, so left cannot participate in a valid pair; advance it. If the sum is too large, right cannot participate, so decrement it. Equality is the answer.
This is more than a loop condition. It explains why moving both pointers after a non-match would be wrong: that could skip a candidate without proving it impossible. In same-direction problems, the invariant changes. For duplicate removal, the prefix nums[0..write - 1] contains exactly one copy of every distinct value seen so far, in sorted order; read scans new input. State the invariant aloud before coding.
JavaScript and TypeScript Templates
1. Opposite-direction search in a sorted array
function twoSumSorted(nums, target) {
let left = 0;
let right = nums.length - 1;
while (left < right) {
const sum = nums[left] + nums[right];
if (sum === target) return [left, right];
if (sum < target) {
left += 1;
} else {
right -= 1;
}
}
return [-1, -1];
}
This template assumes ascending order and returns indices in that sorted array. If a prompt needs original indices, either store value-index pairs before sorting or use a hash map when sorting is not permitted. Confirm that detail before choosing the algorithm.
2. Slow and fast pointers for sorted in-place deduplication
function removeDuplicates(nums) {
if (nums.length === 0) return 0;
let write = 1;
for (let read = 1; read < nums.length; read += 1) {
if (nums[read] !== nums[write - 1]) {
nums[write] = nums[read];
write += 1;
}
}
return write;
}
The return value is the new length. Only nums[0..write - 1] is guaranteed to be the deduplicated result; values after it are intentionally irrelevant. This template is correct because equal values are adjacent in a sorted array.
Useful Variants and Their Rules
| Variant | Pointer state | Movement rule |
|---|---|---|
| Opposite ends | left and right bound viable pairs. | Use sorted order or a proof based on the limiting height/value. |
| Same direction | read scans; write marks accepted output. | Advance write only after accepting the current value. |
| Merge from the back | Two source tails and one destination tail. | Write the larger tail first so unread values are never overwritten. |
| Fix one plus two pointers | An outer index fixes one value; inner pointers find the rest. | Sort first and skip duplicate anchors and duplicate pointer values. |
| Three-way partition | Regions are known-low, unknown, and known-high. | After swapping an unknown high value, inspect the replacement before advancing. |
For 3Sum, sorting costs O(n log n), then each fixed anchor gets one linear two-pointer scan, for O(n2) total. Skip an anchor equal to the previous anchor, and after recording a triplet move past duplicate left and right values. That duplicate policy belongs in the invariant: every reported triplet is unique.
Worked Dry Run: Target Pair
Take nums = [-4, -1, 0, 3, 5, 9] and target = 8. Start with left = 0 and right = 5. The sum is -4 + 9 = 5, too small. No partner at an index at most 5 can make -4 reach 8, so discard index 0 and move left to 1. Now the sum is -1 + 9 = 8, so return [1, 5].
Notice what was not tried: -4 with every other value. The sorted-order proof eliminated all of those possibilities at once. If the first sum had been 12 instead, the symmetric proof would discard the right value because every partner to its left would produce an equal or smaller sum.
Complexity and Edge Cases
| Problem form | Time | Extra space |
|---|---|---|
| Sorted pair search or palindrome scan | O(n) | O(1) |
| Deduplicate or merge in place | O(n + m) | O(1), excluding required output |
| Unsorted pair search after sorting | O(n log n) | Depends on the sort and index preservation |
| 3Sum after sorting | O(n2) | O(1) auxiliary, excluding answers and sort internals |
Test empty and one-element inputs before initializing a right pointer. Check whether equality is allowed at the boundary: a palindrome loop may use left < right, while a center-based condition could allow equality. Use a numeric type appropriate for the language if sums can overflow. For strings, clarify whether punctuation and case should be skipped; normalize or advance past non-alphanumeric characters only when the prompt says so.
Common Failure Modes
- Applying the pair-sum movement rule to an unsorted array, where “too small” does not justify discarding the left value.
- Returning sorted-array indices when the interviewer asked for original indices.
- Using
left <= rightin a pair problem and accidentally using one element twice. - Forgetting duplicate skips in 3Sum, producing repeated triplets.
- Advancing
readbut failing to maintain the accepted prefix in an in-place problem. - Mutating input even though the prompt requires it to remain unchanged.
Deliberate Practice Ladder
- Start with Valid Palindrome and Two Sum II. For each, write the invariant before coding.
- Practice Remove Duplicates from Sorted Array and Move Zeroes to distinguish read/write state from opposite-end state.
- Solve Container With Most Water and explain why moving the taller wall cannot improve the limiting height.
- Solve 3Sum, then redo it with a focus on sort order and duplicate elimination rather than memorizing code.
- Finish with Trapping Rain Water or a three-way partition problem, where the movement proof is less obvious.
After each problem, change one requirement: unsorted input, original indices, unique output, or no mutation. Explaining why the chosen pattern still works—or why it no longer applies—builds transfer skill.
Mock-Interview Explanation Checklist
- Restate whether the input is sorted and whether the output needs values, indices, or an in-place length.
- Give the brute-force baseline, then identify the ordering that lets one pointer movement discard candidates.
- Name both pointer roles and the invariant before presenting the loop.
- Walk through a non-match, explain exactly why one pointer moves, and mention termination.
- State time and auxiliary-space complexity, including sorting when applicable.
- Offer tests for an empty input, duplicate values, no solution, and boundary equality.
Connect This Pattern to the Rest of Your Prep
Two pointers and sliding windows both use boundaries, but a sliding window maintains a contiguous range and usually moves left only to repair a window condition. Dynamic Programming is different: it stores answers to repeated subproblems instead of eliminating candidates with a pointer proof. Continue with the full LeetCode pattern library, then pair these drills with the software engineer interview questions guide.
For personal study and mock interviews, GhOst can be used to rehearse explaining an invariant, compare a solution with a template, and review edge cases. Use assistance in an actual interview or assessment only when the rules explicitly permit it. Review the relevant platform guidance and compatibility information before relying on any tooling.
Frequently Asked Questions
Use Two Pointers when sorted or monotonic order lets a pointer movement safely discard candidates. For an unsorted pair problem that must preserve original indices, a hash map is often the better choice.
With ascending input, the current left value paired with any value at or before the current right value is no larger. It cannot reach the target, so advancing left is safe.
Two Pointers can start at opposite ends, merge sequences, or compact output. Sliding Window is a same-direction specialization that maintains a contiguous subarray or substring and repairs a window condition.
A single sorted pair scan, palindrome check, or in-place compaction is usually O(n) time and O(1) extra space. Sorting an unsorted input adds O(n log n), and 3Sum is O(n squared) after sorting.
