Two Pointers Pattern

All lessons

Learn when to move two indices toward (or across) each other over a sequence, instead of reaching for a hash map — and when a window or map is the better fit.

0/5 practice problems solved

Skip to practice

Recognizing the pattern

Reach for it when you need to

  • the input is sorted, or can be sorted without losing what you need;
  • you are comparing or combining a value near the start with one near the end;
  • you need to remove/partition elements in place without extra memory;
  • a brute-force pair scan is O(n²) but one monotonic property lets both ends move only forward.

Don't use it when

  • Unsorted input where the answer needs arbitrary-index lookups — a hash map (Arrays & Hashing) is faster to reach for.
  • A contiguous range whose size changes — that's Sliding Window, not two fixed pointers.
  • You need every pair, not just the ones a monotonic move can skip.
  • The relationship between elements isn't monotonic in either pointer's direction.

Core mental model

Two pointers only pays off when moving one of them can never miss a better answer. Before advancing the worse side, you must be able to prove no answer using the current worse index and any position past the other pointer could beat what you've already seen — that's what lets you discard a whole side in one step instead of scanning it.

Typical time: O(n)
Typical space: O(1)
Brute-force pairs: O(n²)

Visual trace

Container With Most Water: heights [1, 8, 6, 2, 5, 4, 8, 3, 7]. The shorter side is always moved, since it caps the area and the wall behind it can only help.

area = 8 — move l (shorter side)heights108162235445863778lr
Step 1: l = 0, r = 8 — area = min(1, 7) * 8 = 8. The left wall is shorter, so move l.
area = 49 (best so far) — move rheights108162235445863778lr
Step 2: l = 1, r = 8 — area = min(8, 7) * 7 = 49, the best so far. Now the right wall is shorter, so move r.

Worked example: Container With Most Water

heights = [1, 8, 6, 2, 5, 4, 8, 3, 7]. Track the best area seen while l and r converge.

Step-by-step trace of Container With Most Water
l / rmin heightwidthareamove
0 / 8188move l (shorter)
Key step: 1 / 87749 (best)move r (shorter)
1 / 73618move r (shorter)

Invariant: Before moving a pointer, every skipped position on that side is provably no better: the wall height caps the area, and the other wall cannot get any farther away.

Reusable template

The shape to adapt for a new problem in this pattern.

python

def solve(heights):
    l, r = 0, len(heights) - 1
    best = 0
    while l < r:
        area = min(heights[l], heights[r]) * (r - l)
        best = max(best, area)
        if heights[l] < heights[r]:
            l += 1
        else:
            r -= 1
    return best

Common mistakes

  • Moving the taller wall instead of the shorter one (loses the proof that no skipped index could help).
  • Forgetting to update the running best before moving a pointer.
  • Using two pointers on unsorted data where the monotonic property doesn't actually hold.
  • Off-by-one loop bounds — using <= when l and r must never cross the same index twice.
  • Reaching for two pointers when the window size (not two independent ends) is what's actually changing — that's Sliding Window.

Practice: easy → medium → timed check

Solve in order; each step adds one decision to the same pattern.

  1. 1. Palindrome String

    Easy
    Not solved yet

    Converge from both ends

  2. 2. Is Subsequence

    Easy
    Not solved yet

    Two pointers, different speeds

  3. 3. Move Zeroes

    Easy
    Not solved yet

    In-place partition pointer

  4. 4. Container With Most Water

    Medium
    Not solved yet

    Discard the shorter wall

  5. 5. Trapping Rain Water

    Hard
    Not solved yet

    Track max-so-far from both sides

Final timed check

Run a 30-minute mock covering this pattern; state the invariant before coding, then finish with complexity and edge cases.

Start timed mock

Next lesson

Sliding Window