Beginner~9 min

Two Pointers

How to check every pair worth checking, without checking every pair. (“Pointer” here just means a position in the list — a number like 0 or 7. Nothing to do with memory addresses.)

The job

You have a sorted list of numbers and a target. Find two numbers in it that add up to exactly that target — and report where they are.

The obvious way

Take the first number, try it with every other number. Then the second number with every other. And so on — a loop inside a loop.For 8 numbers that is 28 pairs. For 10,000 it is about 50 million.

The idea

Put one finger on the smallest number and one on the largest, and add them. If the total is too big, the biggest number is too big to ever work — drop it. If the total is too small, the smallest number is too small to ever work — drop it. Either way one number is gone for good, so the list shrinks on every single check.

Think of it like this. Two people walking toward each other from opposite ends of a corridor, and they must meet at exactly the right spot. Every step, whoever is clearly in the wrong place moves — never both. Because they only ever walk inward, they can never revisit ground, so they cover the whole corridor between them in one pass instead of one person pacing it end to end, repeatedly.

Looking for a pair that adds to 29

left
right
sum
checks
Still in play
/ 8

8 numbers, sorted smallest to largest. Find two of them that add up to exactly 29.

0.0s/ 18.6s
two_sum_sorted.pypython
def two_sum_sorted(nums, target):
    left, right = 0, len(nums) - 1

    while left < right:
        total = nums[left] + nums[right]

        if total == target:
            return [left, right]
        elif total < target:
            left += 1
        else:
            right -= 1

    return []

This run

Found 11 + 18 at indices [2, 4] in 6 checks — trying every pair would have been up to 28.

Try:

The two names you just watched

left and right

Just two positions in the list — the smallest number still worth considering, and the largest. Everything outside them has already been ruled out.

Only one moves

Each check retires exactly one number, so the gap always closes by one. That is why it finishes in one pass instead of looping over the list again and again.

Why sorted matters

Sorted order is what makes "too big" mean "and every number further right is worse". Without it, ruling out a value tells you nothing, and you are back to trying every pair.

How to spot a two-pointer problem

The tell: You are about to write a loop inside a loop over one sorted list, and you can argue that one end is definitely useless. Every time you can rule out an end, you can walk inward instead of starting over.

Reach for it when

  • A sorted list, and you want a pair, triple, or a stretch that satisfies something.
  • Your first instinct is a nested loop over the same array.
  • The two ends of the data mean opposite things (smallest / largest, front / back).
  • You are comparing something to its mirror — palindromes, reversing in place.

Not this when

  • The list is unsorted and order matters, so “too big” tells you nothing.
  • You need pairs from two unrelated arrays — that is usually a hash map.
  • You need every pair, not one — no end can ever be ruled out.
  • The answer depends on values you already walked past.

Practice

3 easy · 3 hard
LeetCodeO(n) / O(1)

The lesson, exactly. Note the 1-indexed answer the problem asks for — that catches more people than the algorithm does.

LeetCodeO(n) / O(1)

Same converging pointers, comparing characters instead of summing numbers — skip anything non-alphanumeric as you walk.

LeetCodeO(n) / O(n)

The largest square is always at one end or the other, so fill the answer from the back while the pointers close in.

15. 3SumHard-ish (Medium)
LeetCodeO(n²) / O(1)

Fix one value, then run this exact two-pointer sweep on the rest. The real difficulty is skipping duplicates cleanly.

LeetCodeO(n) / O(1)

Carry a running max from each side and always move the pointer standing behind the smaller max — that side is the one whose answer is already decided.

LeetCodeO(n) / O(1)

Moving the taller wall inward can never help — width shrinks and height is still capped by the shorter one. That is the whole proof.