VISHAL CHERUPALLYVC
Chapter 01  ·  Arrays & Searching

Before Two Pointers, There Was A Problem.

Every elegant technique was born out of frustration with something slow, ugly, and broken. Let's feel that frustration first.

scroll to begin

A Simple Question.
An Obvious Answer.

You are handed a sorted array of integers. A number target is given to you. Your job:

📋 Problem — Two Sum (Sorted Input)

Given a sorted array nums and an integer target, return the indices of the two numbers that add up to target.

Input:  nums = [-4, -1, 1, 3, 5, 8, 11], target = 9
Output: indices [2, 5]  →  nums[2] + nums[5] = 1 + 8 = 9  ✓

Stop reading for one second. What's the first solution that comes to your mind?

The Instinct.
Check Everything.

Most people, including experienced developers on a first pass, reach for this:

def two_sum_brute(nums, target):
    n = len(nums)
    for i in range(n):             # fix first number
        for j in range(i + 1, n):   # try every other number
            if nums[i] + nums[j] == target:
                return [i, j]
    return []

This is correct. It passes every test case. It will be accepted on LeetCode for small inputs.

It is also, fundamentally, thoughtless.

🔍
Brute force says: “I don't trust the structure of this data. So I'll check every possibility.”

But the array is sorted. Sorted data has a shape, a gradient, a direction. Ignoring that is like navigating a one-way street going both ways at once.

Let's Count
The Operations.

For an array of size n, the nested loop runs n × (n-1) / 2times. That's O(n²).

n (size)Brute ops2-ptr ops
1004,950100
1,000499,5001,000
10,000~50 Million10,000
100,000~5 Billion100,000
Approach
Time
Space
Brute Force
O(n²)
O(1)
Two Pointers
O(n)
O(1)

A real dataset — say, a list of 100,000 stock prices where you want a pair summing to a target profit — the brute force would compute 5 billion comparisons. At 10⁹ operations/second, that's 5 seconds of waiting. In a live system, that's a timeout. That's a failure.

TRY IT — Array size (n):
↑ Enter n and a target. Watch operations pile up.

The Array Is
Trying To Tell You Something.

Look at the sorted array again. Stare at it.

L-4
0
-1
1
1
2
3
3
5
4
8
5
R11
6

There are three outcomes when you pick any two numbers from a sorted array and sum them:

01
Sum is too big. The right number is too large. Move the right pointer ← left. Every pair to the right is already too big.
02
Sum is too small. The left number is too small. Move the left pointer → right. Every pair to the left is already too small.
03
Sum equals target.You're done. Return the indices.

Each comparison eliminates an entire direction. You're not checking all possibilities — you're reasoning your way to the answer.

💡
The core idea: When data has a monotonic structure (sorted, distance-based, frequency-ordered), you can replace a nested search with a single pass using two indices that squeeze inward — each step guaranteed to make progress.

Enter:
Two Pointers.

We place one pointer at the far left, one at the far right. They march toward each other, each step informed by what we see.

L=0-4
0
-1
1
1
2
3
3
5
4
8
5
R=611
6
def two_sum_sorted(nums, target):
    L, R = 0, len(nums) - 1   # start at opposite ends

    while L < R:                 # until pointers meet
        current = nums[L] + nums[R]

        if current == target:
            return [L, R]          # found it
        elif current < target:
            L += 1                  # sum too small → move left up
        else:
            R - = 1                  # sum too large → move right down

    return []                    # no pair found

Watch it execute on nums = [-4, -1, 1, 3, 5, 8, 11], target = 9:

1
L=0, R=6 → -4 + 11 = 7. Too small. Move L right.
2
L=1, R=6 → -1 + 11 = 10. Too big. Move R left.
3
L=1, R=5 → -1 + 8 = 7. Too small. Move L right.
4
L=2, R=5 → 1 + 8 = 9. ✓ Target found! Return [2, 5].

4 comparisons instead of 21. The array had 7 elements — brute force would check C(7,2) = 21 pairs. Two pointers found the answer in 4.

the anatomy of the pattern

🎯 When It Works

Sorted arrays, or data with a monotonic property — if you can reason "moving this pointer increases/decreases the value," you can use two pointers.

⚙️ The Invariant

At every step, the answer (if it exists) is always between L and R. Neither pointer ever skips past it. This is the proof of correctness.

📉 Why O(n)

L and R together make at most n moves total. Every iteration moves at least one pointer. The loop terminates in ≤ n steps.

🧬 The Variants

Same-direction (fast/slow), opposite-direction (squeeze), multiple arrays, sliding window — all cousins of the same core idea.

You Now
Know Why.

The problem wasn't invented to have a clever trick. The trick exists because brute force is genuinely painful at scale. Now every problem we solve will make sense — not just what to do, but why this and not that.

← Back to Python track