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