Skip to content
← All posts

The Two Pointers Pattern: A Complete Guide

8 min read
patternsarraysinterviews

You have a sorted array and you need to find two numbers that add up to a target. The brute force approach checks every pair: pick one element, scan the rest. That is O(n^2). For a million elements, you are looking at half a trillion operations.

But the array is sorted. That is a huge piece of information that brute force completely ignores. The two pointers pattern exploits it to solve the problem in a single pass.

When to use two pointers

The two pointers pattern shows up whenever you need to search for pairs or sections in a sequence, and there is some ordering or structure you can take advantage of. The classic signals:

  • The input is sorted (or can be sorted without breaking the problem)
  • You need to find a pair of elements that satisfy some condition
  • You need to compare elements from both ends of a sequence
  • The problem involves partitioning or rearranging elements in place

If you see "sorted array" and "find two elements" in the same sentence, two pointers should be the first thing you reach for.

The core idea

Place one pointer at the start of the array and one at the end. Look at the sum (or whatever relationship you are checking). If the sum is too small, the left pointer needs to move right to pick a bigger number. If it is too big, the right pointer needs to move left to pick a smaller number. Because the array is sorted, you know exactly which direction to go.

Each step eliminates an entire row or column of possibilities. You never need to backtrack. The pointers converge toward each other, and you are done when they meet.

1031527394115LR
A sorted array with two pointers starting at opposite ends, converging toward the middle.

Walking through two-sum on a sorted array

Given the sorted array [1, 3, 5, 7, 9, 11], find two numbers that sum to a target. We will use L (orange) for the left pointer and R (green) for the right pointer.

The first walkthrough targets sum = 10. The second shows what happens when the target is 8. The third shows a case where the sum is too small and L has to move right instead.

Step 1: L=0, R=5. nums[0] + nums[5] = 1 + 11 = 12. Too big, move R left.

1357911LR

sum = 12 > 10. Move R left to shrink the sum.

Step 2: L=0, R=4. nums[0] + nums[4] = 1 + 9 = 10. Found it!

1357911LR

sum = 10. Target found! Return indices [0, 4].

What if target were 8? Continue: L=0, R=4. Sum=10, too big. Move R left.

1357911LR

sum = 10 > 8. Move R left.

L=0, R=3. nums[0] + nums[3] = 1 + 7 = 8. Found it!

1357911LR

sum = 8. Target found! Return indices [0, 3].

What if target were 14? L=0, R=5. Sum=12, too small. Move L right.

1357911LR

sum = 12 < 14. Move L right to increase the sum.

Notice how each step either moves L right or R left. The pointers never move backward. They start at opposite ends and converge, so you do at most n steps total. That is O(n).

Why does this work? Because the array is sorted. When nums[L] + nums[R] is too big, every pair that includes nums[R] with a larger left index would be even bigger. So you can safely eliminate nums[R] from consideration. The same logic applies in the other direction.

The code

Here is two-sum on a sorted array in Python:

def two_sum_sorted(nums, target):
    left, right = 0, len(nums) - 1

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

        if current_sum == target:
            return [left, right]
        elif current_sum < target:
            left += 1    # need a bigger sum
        else:
            right -= 1   # need a smaller sum

    return []  # no pair found

Four lines of real logic inside the loop. Compare the sum, move the appropriate pointer. That is the entire pattern.

The while left < right condition is important. If you use left <= right, the pointers can land on the same element, and you would be using a single number twice. Unless the problem explicitly allows that, stick with strict inequality.

Here is a slightly more complex example: three-sum. Find all unique triplets that sum to zero. The trick is to fix one element, then run two pointers on the rest.

def three_sum(nums):
    nums.sort()
    result = []

    for i in range(len(nums) - 2):
        # Skip duplicate first elements
        if i > 0 and nums[i] == nums[i - 1]:
            continue

        left, right = i + 1, len(nums) - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]

            if total == 0:
                result.append([nums[i], nums[left], nums[right]])
                # Skip duplicates
                while left < right and nums[left] == nums[left + 1]:
                    left += 1
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1
                left += 1
                right -= 1
            elif total < 0:
                left += 1
            else:
                right -= 1

    return result

Same core pattern, just wrapped in an outer loop. The two pointer scan inside is identical to two-sum.

Common variations

Two pointers is not a single trick. It is a family of techniques that share the same idea: use two indices to avoid redundant work. Here are the main flavors.

1. Converging pointers (opposite ends)

This is what we have been doing. One pointer starts at the beginning, one at the end, and they move toward each other. Used for:

2. Same-direction pointers

Both pointers start at the beginning and move in the same direction, but at different speeds or with different roles. This is closely related to the sliding window pattern but without the "window" framing. Used for:

  • Remove duplicates from sorted array (one pointer for reading, one for writing)
  • Move zeroes to the end
  • Merge sorted arrays
  • Partition problems (Dutch national flag)
def remove_duplicates(nums):
    if not nums:
        return 0

    write = 1
    for read in range(1, len(nums)):
        if nums[read] != nums[read - 1]:
            nums[write] = nums[read]
            write += 1

    return write

3. Fast and slow pointers

One pointer moves one step at a time, the other moves two steps. This is the go-to technique for linked list cycle detection (Floyd's algorithm), but it also shows up in array problems.

  • Detect a cycle in a linked list
  • Find the middle of a linked list
  • Find the start of a cycle
  • Happy number detection
def has_cycle(head):
    slow = fast = head

    while fast and fast.next:
        slow = slow.next
        fast = fast.next.next
        if slow == fast:
            return True

    return False

Fast and slow pointers are sometimes treated as their own pattern rather than a subtype of two pointers. Either way, the underlying idea is the same: two indices moving through the data at different rates to reveal structure you could not see with a single pass.

Common mistakes

These are the bugs I see most often with two pointers problems.

1. Forgetting the array needs to be sorted.

Converging two pointers only works on sorted data. If the problem gives you an unsorted array, you need to sort it first (and account for the O(n log n) sorting cost). If the problem requires you to return original indices, sorting will break that, and you should use a hash map instead.

2. Skipping duplicates incorrectly.

In problems like three-sum where you need unique results, skipping duplicates is essential but easy to get wrong. The skip logic needs to happen after you have found a valid result, not before. And you need to skip duplicates for all the pointers involved.

3. Using <= instead of < in the while condition.

Unless the problem explicitly says you can reuse the same element, your pointers should never be at the same index. Using left <= right instead of left < right is a subtle bug that passes most test cases but fails edge cases.

4. Moving the wrong pointer.

When the sum is too small, move left (to get a bigger number). When it is too big, move right (to get a smaller number). Mixing these up gives you an infinite loop or wrong answers. If you get confused, think about it concretely: "I need a bigger sum. The array is sorted. Moving left forward gives me a bigger number."

How to recognize it in interviews

Here are the patterns to watch for:

  • The input is a sorted array (or the first step is to sort it)
  • The problem mentions pairs or two elements satisfying a condition
  • You see words like "two sum," "pair," "complement," or "closest to target"
  • The problem asks you to do something in-place with O(1) extra space
  • A linked list problem asks about cycles or finding the middle

If you see a sorted array and a pair-finding problem, try two pointers before anything else. It is almost always the intended approach, and it runs in O(n) time with O(1) space.

Two pointers and sliding window are closely related. The difference: sliding window maintains a contiguous range and tracks aggregate state (sum, character counts). Two pointers typically just look at the elements at the pointer positions. If you need to examine everything between the pointers, you probably want a sliding window.

Reading about it is not enough

Understanding two pointers from a blog post is the easy part. What is hard is writing the code cleanly, from scratch, three months from now when it shows up in your interview.

That gap between recognition and recall is where most people fail. You read a solution, nod along, maybe copy it into a notes file. But when the pressure is on, you fumble the pointer movement logic or forget to handle duplicates.

Spaced repetition closes this gap. You write the code from scratch at increasing intervals. First after a day, then three days, then a week. Each repetition strengthens the pattern until it is automatic. No more "I have seen this before but I cannot remember the details."

Two pointers is one of about 60 fundamental building blocks that appear across hundreds of coding problems. Learning them individually, through repeated practice, is far more effective than grinding through random problems and hoping the patterns stick.

Related posts

  • The Sliding Window Pattern - A closely related technique where both pointers move in the same direction to track a contiguous range
  • Hash Map Patterns - When the array is unsorted, hash maps solve pair-finding problems like two-sum without needing to sort first
  • Binary Search - Another O(log n) technique for sorted data that pairs well with two pointers

New to algorithms? Start with our visual What is an Algorithm? guide, or learn Big O Notation to understand time complexity.