← Articles

Two Pointers Algorithm: When to Use Which Direction

The two pointers algorithm solves array and string problems by walking two index variables through the input instead of comparing every element against every other one. It replaces a brute-force nested loop, usually O(n squared), with a single pass that runs in O(n) and needs no extra data structure. The trick has two main setups on arrays: one pointer starting at each end and moving toward the middle, or both pointers starting together and moving in the same direction at different speeds. A third variant, fast and slow pointers on a linked list, belongs to the same family but solves a different class of problem, and this guide points you to where that one is covered in depth rather than repeating it here. What follows is how to recognize which setup a problem wants, worked examples for each one, and the mistakes that turn a clean two-pointer solution into a buggy one under interview pressure.

How Do You Recognize a Two Pointers Problem?

You recognize a two pointers problem by a small set of signals that tend to show up together in the problem statement. The clearest one is a sorted array combined with a request for a pair, since sorting gives you the ordering guarantee that lets moving a pointer eliminate a whole range of impossible answers at once instead of checking them one by one.

A few concrete phrases tend to show up in real two pointers problems:

  • "Given a sorted array, find a pair that sums to a target value."
  • "Return true if the string reads the same forwards and backwards."
  • A request to modify an array in place and return a new length, such as removing duplicates or shifting zeros to the end.
  • A geometry-flavored setup asking for the largest area, container, or distance between two boundaries.
  • Any problem where the brute-force answer checks every pair with a nested loop and the interviewer follows up by asking for something faster without extra memory.

Once you spot one of these signals, ask yourself a second question before committing to the pattern: does the input have an order you can exploit, either because it is already sorted or because you can sort it yourself without breaking the problem? A yes points you toward two pointers. A problem that instead wants every possible pair regardless of order, or where sorting would destroy information the answer depends on (the original index of each element, for instance), is telling you two pointers is the wrong tool, and a hash map is usually the better one.

Opposite Ends or Same Direction: Which Setup Do You Need?

You need the opposite-ends setup when the problem asks about a pair drawn from a sorted array or a boundary comparison, and the same-direction setup when the problem asks you to build or filter a sequence as you scan it once from left to right.

The opposite-ends setup starts one pointer at index 0 and the other at the last index, then moves whichever one the current comparison tells you to move. Take the classic version: given a sorted array, find two numbers that add up to a target value.

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

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

    return []

The move that confuses people the first time they see it is why moving `left` forward when the sum is too small is safe. Since the array is sorted, `nums[right]` is already the largest value left in the search space, so if `nums[left] + nums[right]` still falls short of the target, no pairing of `nums[left]` with any smaller value could possibly reach the target either. That pairing is provably useless, and advancing `left` is the only move that keeps the search space shrinking without throwing away a pair that could still work.

The same-direction setup starts both pointers at the beginning, but one of them (often called a write pointer) only advances when the current element passes some test, while the other (the scan pointer) advances on every step. Removing duplicates from a sorted array in place is the cleanest example: the scan pointer checks every element, and the write pointer only moves and copies a value forward when it differs from the last value that was kept.

def remove_duplicates(nums):
    if not nums:
        return 0

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

    return write

Both setups run in O(n) time and O(1) extra space, but they solve different shapes of problem, and mixing them up (reaching for opposite ends on a problem that actually wants a same-direction scan) is one of the more common ways candidates stall out mid-interview.

How Do You Solve Container With Most Water With Two Pointers?

You solve container with most water by starting one pointer at each end of the array and always moving the pointer at the shorter line, because the shorter line is the only thing that could possibly be limiting the current area. Given an array where each value represents the height of a vertical line at that index, the problem asks for the maximum area you can enclose between any two lines, where the area equals the distance between them multiplied by the shorter of the two heights.

def max_area(heights):
    left, right = 0, len(heights) - 1
    best = 0

    while left < right:
        width = right - left
        height = min(heights[left], heights[right])
        best = max(best, width * height)
        if heights[left] < heights[right]:
            left += 1
        else:
            right -= 1

    return best

The part worth defending out loud is why it's safe to throw away the shorter line's current position instead of trying every combination. Once you fix the shorter line as the limiting height, moving the taller line's pointer inward can only shrink the width while the height stays capped at the same shorter value, so that move can never improve the answer. Moving the shorter line's pointer inward is the only move that has any chance of finding a taller line that raises the limiting height, even though it also shrinks the width. That is the entire proof behind why a single O(n) pass finds the true maximum instead of just a local one, and it's exactly the kind of follow-up question interviewers ask when a candidate gets the code right but can't explain why the greedy step is correct.

What About Problems With Three or More Pointers?

Problems that ask for a triplet, like finding three numbers that sum to zero, extend the opposite-ends setup by fixing one value and running a two-pointer scan on the rest. Sort the array first, then for each index `i`, run the standard opposite-ends scan on the remaining slice to find pairs that sum to the negative of `nums[i]`.

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

    for i in range(len(nums) - 2):
        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]])
                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

The sort costs O(n log n), and the outer loop combined with an inner two-pointer scan costs O(n squared), which together still beats the O(n cubed) brute force of checking every triplet directly. The two `while` loops that skip past repeated values after a match are not optional cleanup, they are the entire reason this returns unique triplets instead of the same answer several times over, and skipping them is the single most common bug in a live 3Sum attempt.

How Is This Different From Sliding Window?

Two pointers and sliding window are related but answer different questions: sliding window tracks a contiguous range and asks what the best range looks like, while an opposite-ends two pointers problem compares two boundary elements directly and asks which one to move, with no running range in between. Our sliding window guide covers this same comparison from the other direction and goes deep on when a moving range needs to track a sum, a count, or a frequency map as it grows and shrinks.

The practical test is whether you find yourself maintaining state about everything currently between your two pointers. If you are, you're almost certainly doing sliding window, even if both pointers happen to be moving the same direction. If you're only ever comparing the two boundary values against each other or against a target, and nothing about the space between them matters, you're doing a same-direction or opposite-ends two pointers problem instead. Confusing the two rarely breaks the code outright, but it usually means you're tracking state you don't need, or missing state you do.

Where Does the Fast and Slow Pointer Variant Fit?

Fast and slow pointers, sometimes called Floyd's algorithm, belong to the same two pointers family but solve a completely different problem: detecting a cycle or finding the middle of a linked list by moving one pointer twice as fast as the other. It shows up constantly in linked list questions and rarely in array questions, because an array gives you direct index access that makes the speed trick unnecessary. Our linked list interview questions guide walks through the fast and slow setup in depth, including cycle detection and the palindrome check that combines it with in-place reversal, so this guide won't repeat that walkthrough here. The short version worth remembering is that if the word "linked list" appears anywhere in the problem, fast and slow pointers is the variant to reach for, not the array setups covered above.

What Mistakes Sink a Two Pointers Answer in an Interview?

Forgetting that the input needs to be sorted first is the most common mistake, especially in a 3Sum-style problem where the sort is easy to skip past mentally because the array "already looks kind of ordered" in the example given. Say out loud whether the input is sorted, and if it isn't, sort it and account for that O(n log n) cost in your final complexity claim.

Moving both pointers on the same step, instead of moving exactly one based on a comparison, is the second common failure, and it usually comes from copying the shape of a sliding window loop without checking whether this problem actually needs both pointers to advance together. In an opposite-ends problem, only one pointer should move per iteration, chosen by the comparison result, not both.

An off-by-one error on the loop condition, using `<=` where the logic needs `<` or the reverse, shows up constantly when the two pointers are allowed to meet at the same index versus when they must stay strictly apart. Decide which one your problem needs before you write the loop: a palindrome check can let the pointers meet or cross, but a pair-sum search on distinct indices needs `left < right` so the same element never pairs with itself.

Skipping the duplicate-avoidance step in a triplet or multi-pointer problem is the mistake that turns a correct algorithm into one that returns the right values wrapped in duplicate entries, which usually fails a test case the candidate didn't think to check by hand. If the problem asks for unique combinations, the duplicate-skipping `while` loops from the 3Sum example above are not an afterthought, they're part of the core logic.

What Should You Say Out Loud While You Solve One?

Name which setup you're using before you write any code. Say "this is sorted and asks for a pair, so I'll start one pointer at each end" or "this wants me to filter in place while scanning once, so I'll use a write pointer and a scan pointer", and give the one-sentence reason a nested loop would be slower. That sentence tells the interviewer you recognized the pattern instead of stumbling into a working solution by trial and error.

As you write the loop, narrate the comparison that decides which pointer moves and why the other one staying still doesn't lose any valid answers, the same proof-sketch reasoning covered above for container with most water. Trace through a short example by hand once the code is on the screen, including an edge case like an array with two elements or an array with no valid pair, and state the final complexity out loud: "this runs in O(n) time since each pointer moves across the array at most once, and O(1) extra space since I'm only tracking a fixed number of indices". Interviewers grade this pattern heavily on whether you can defend that the pointers never move backward, not just on getting the right output.

Which Practice Problems Actually Build the Pattern-Recognition Skill?

A good practice set covers both setups and the multi-pointer extension, in an order that adds one new piece of mechanics at a time rather than jumping straight to the hardest variant. Start with the sorted-pair-sum problem to get the opposite-ends comparison logic under your fingers, then move to container with most water to practice the greedy proof that justifies moving the shorter pointer. Remove duplicates from a sorted array is the cleanest same-direction example, and valid palindrome combines a string comparison with the opposite-ends setup in a form that shows up constantly. 3Sum is the natural next step once the two-pointer core feels automatic, since it's really the pair-sum problem run inside an outer loop.

Our curated question bank draws from real onsite reports rather than an unfiltered public archive, so the two pointers problems you find there reflect what companies are actually asking right now instead of a static list that stopped updating years ago.

Where Two Pointers Fits Into Your Broader Pattern Prep

Two pointers is one of a small set of patterns that covers most of what shows up in a real coding interview, and it overlaps heavily with two others worth studying alongside it. Our guide to spotting patterns before you code covers how two pointers relates to sliding window, binary search, and the rest of that core set, and walks through the same signal-recognition habit this guide applies specifically to paired indices.

If the complexity claims in this guide felt shaky, especially why a same-direction pass with an inner check still counts as O(n), our breakdown of how to analyze time complexity covers that reasoning in more depth, including how interviewers actually probe it with follow-up questions. And if the problem you're facing involves a linked list rather than an array or string, our linked list interview questions guide is where the fast and slow pointer variant of this same family gets the full worked treatment.

Frequently Asked Questions

What is the two pointers algorithm?

The two pointers algorithm is a technique for solving array and string problems by moving two index variables through the input instead of comparing every element against every other one with a nested loop. It typically turns an O(n squared) brute-force solution into an O(n) one using O(1) extra space.

When should I use opposite-ends pointers instead of same-direction pointers?

Use opposite-ends pointers when the problem asks about a pair or boundary comparison in a sorted array, such as finding two numbers that sum to a target or maximizing the area between two lines. Use same-direction pointers when the problem asks you to filter or build a sequence in a single left-to-right scan, such as removing duplicates in place.

Is the two pointers technique the same as sliding window?

Sliding window is a specific case of two pointers where both pointers move in the same direction while maintaining running state about everything currently between them, such as a sum or a frequency count. A same-direction or opposite-ends two pointers problem usually compares only the boundary values themselves, with no running state about the range in between.

Does the two pointers algorithm work on an unsorted array?

Most two pointers problems need a sorted array to guarantee that moving a pointer never skips a valid answer, so if the input isn't already sorted, you generally sort it first and account for that O(n log n) cost. A few same-direction problems, like removing duplicates from an already-sorted array, only work because the input is sorted, and don't apply at all to unsorted input.

What is the fast and slow pointer variant used for?

Fast and slow pointers, also called Floyd's algorithm, move through a linked list at two different speeds to detect a cycle or find the middle node in a single pass without extra memory. It belongs to the same two pointers family covered in this guide but applies to linked lists rather than arrays, and our linked list interview questions guide covers it in depth.

What's a good first two pointers problem to practice?

Start with finding a pair that sums to a target in a sorted array to learn the opposite-ends comparison, then move to container with most water once that feels automatic, since it adds the greedy proof that justifies which pointer to move. Save 3Sum for after both of those, since it wraps the same pair-sum logic inside an outer loop.