Two Pointers

Mediumarrays · two pointers · sorting · greedy⏱ 6 min readUpdated

Move two indices through a sequence, usually from opposite ends of sorted data, so each step safely discards many candidate pairs and an O(n^2) search becomes O(n).

PatternTwo Pointers

Instinct Sorted input, a pair/triple hitting a target, or something symmetric? Put one pointer at each end and move the one that can only improve things.

Memory hack Squeeze from both ends - every step throws away a whole row of the brute-force grid.

At a glance

When to use it

  • The input is sorted, or may be sorted without losing what the answer needs
  • You need a pair or triple that hits a target sum
  • The answer depends on two boundaries such as width times height or left and right maxima
  • Two sorted sequences must be merged, or something symmetric compared in place

Key idea

Start one pointer at each end and move only the one that cannot do better where it is, so every comparison discards a whole row of candidate pairs and an O(n^2) pair search becomes O(n) with O(1) space.

Typical complexity

O(n) time, O(1) space (O(n log n) if you sort first)

How to spot it If the problem says "sorted array" and asks for a pair, a triple or the best area between two positions, think two pointers first.

The instinct

Brute force over pairs checks every cell of an n × n grid. Two pointers works when some ordering lets you prove that a whole row or column of that grid is useless after a single comparison. With sorted data and a target sum, if a[i] + a[j] is too small, then pairing a[i] with anything left of j is also too small, so i can advance. One comparison, n candidates eliminated.

There are three common shapes: converging (start at both ends, walk inward), parallel (one pointer per input array, as in merging), and reader/writer (both move forward at different speeds). This guide focuses on the first two; the fast/slow linked-list version is its own pattern.

Recognise it

Signal in the problem Why it points here
"Input array is sorted" plus "find a pair" Order tells you which pointer to move after each comparison.
"Two numbers / three numbers that sum to target" Fix one element, run a converging pair on the rest (3Sum).
"Maximise area / width × height between two lines" Start at maximum width; only moving the shorter side can help.
"Trapped water", "left max and right max" Process the side with the smaller boundary; its water level is already known.
"Merge two sorted arrays", "in place, fill from the back" One pointer per array, write from the largest end to avoid overwriting.
"Palindrome", "reverse in place" Symmetry: compare or swap mirrored positions.
"O(1) extra space" where a hash set would be easy Two pointers on sorted data replace the set.

Not this pattern if...

  • The data is unsorted and you must keep original indices: use a hash map (Two Sum) unless sorting is allowed.
  • You need the best contiguous window under a constraint: that is Sliding Window (same-direction pointers with a validity rule).
  • The pointers walk a linked list to find cycles or midpoints: that is Fast & Slow Pointers.
  • You need all pairs, not one best or one matching pair: the output itself may be O(n^2).

Memory hack

Tip

Squeeze from both ends. Every step throws away a whole row of the brute-force grid. Picture two people carrying a long plank through a narrow hallway towards each other: at each step, only the one who is "losing" (too small a sum, the shorter wall) shuffles forward. They meet in the middle after at most n steps, never retracing.

Say it out loud in the interview:

  1. "Is the data sorted, or may I sort it without losing what the answer needs?"
  2. "After comparing a[l] and a[r], which pointer's move is provably safe, and why can't the other one help?"
  3. "Loop while l < r; skip duplicates if the output must be unique."

How it works

Write down why discarding is safe; that justification is what interviewers grade.

  • Pair sum (sorted): sum too small → l++ (the smallest value can't reach the target with anything). Too big → r--.
  • Container With Most Water: area is limited by the shorter line. Moving the taller line can only shrink width without raising the limit, so always move the shorter one.
  • Trapping Rain Water: keep leftMax and rightMax. If leftMax < rightMax, the water above l is decided by leftMax regardless of what lies between, so settle l and advance it.

Trace for Two Sum II on [2, 3, 5, 8, 11], target 13:

l r a[l] + a[r] decision
0 4 2 + 11 = 13 found (1-indexed answer [1, 5])

And with target 10:

l r sum decision
0 4 13 too big → r--
0 3 10 found
flowchart TD
  S["l = 0, r = n - 1"] --> C{"l < r ?"}
  C -- no --> E[No answer]
  C -- yes --> V["Evaluate a[l] + a[r]"]
  V --> T{Matches goal?}
  T -- yes --> R[Record / return]
  T -- too small --> L["l++"]
  T -- too big --> Q["r--"]
  L --> C
  Q --> C

Java template

java
import java.util.*;

class TwoPointerToolkit {
    // Converging pointers on sorted data.
    static int[] pairSum(int[] a, int target) {
        int l = 0, r = a.length - 1;
        while (l < r) {
            int sum = a[l] + a[r];                 // problem-specific evaluation
            if (sum == target) return new int[]{l, r};
            if (sum < target) l++;                 // left side can't help with any r' <= r
            else r--;                              // right side can't help with any l' >= l
        }
        return new int[]{-1, -1};
    }

    // Fix one, converge on the rest, skip duplicates (k-sum skeleton).
    static List<List<Integer>> tripletsSummingTo(int[] nums, int target) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        for (int i = 0; i < nums.length - 2; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) continue;       // unique anchors
            int l = i + 1, r = nums.length - 1;
            while (l < r) {
                long sum = (long) nums[i] + nums[l] + nums[r];
                if (sum == target) {
                    res.add(Arrays.asList(nums[i], nums[l], nums[r]));
                    while (l < r && nums[l] == nums[l + 1]) l++;  // skip equal lefts
                    while (l < r && nums[r] == nums[r - 1]) r--;  // skip equal rights
                    l++; r--;
                } else if (sum < target) l++;
                else r--;
            }
        }
        return res;
    }

    // Parallel pointers, writing from the back (merge into a with spare room).
    static void mergeFromBack(int[] a, int m, int[] b, int n) {
        int i = m - 1, j = n - 1, w = m + n - 1;
        while (j >= 0) {
            if (i >= 0 && a[i] > b[j]) a[w--] = a[i--];
            else a[w--] = b[j--];
        }
    }
}

Complexity

  • Converging scan: O(n) time, O(1) space; each step moves a pointer inward, so at most n steps.
  • If you must sort first: O(n log n) overall.
  • 3Sum: O(n log n) sort + n anchors × O(n) scan = O(n^2); k-Sum generalises to O(n^(k-1)).
  • Merge from the back: O(m + n) time, O(1) extra space.

Variations

  • Target pair on sorted data: Two Sum II - Input Array Is Sorted.
  • Anchor + pair (k-Sum): sort, fix one value, converge on the rest, skip duplicates. 3Sum.
  • Greedy shrink by the weaker side: Container With Most Water.
  • Boundary maxima from both ends: Trapping Rain Water (an O(1)-space alternative to prefix/suffix max arrays or a stack).
  • Merging sorted sequences: Merge Sorted Array, filling from the end to avoid overwriting.
  • Partitioning: Dutch National Flag style three-way partition with low/mid/high pointers.
  • Symmetry checks: palindromes and in-place reversal.

Common mistakes

  • Applying converging pointers to unsorted data, where the "safe to discard" argument does not hold.
  • Sorting when the problem wants original indices and forgetting to keep them (pair values with indices first).
  • In 3Sum, skipping duplicates before recording a match, or only skipping on one side, producing repeated triplets.
  • Using l <= r so an element pairs with itself.
  • Overflowing int when adding three large values; widen to long.
Warning

In Container With Most Water, moving the taller line is never useful: width shrinks while height is still capped by the shorter line. If you can't explain why the pointer you move is safe, the interviewer will assume you memorised the answer.

All practice problems

5 problems on this site use this pattern, easiest first. Solve them in order and say the instinct out loud before you code.

My notes saved in this browser