Back to blog
Two Pointers pattern illustration showing two arrow cursors on a sorted array
Coding Patterns

Two Pointers: The Pattern Inside Sorted Array Problems

May 24, 2026 8 min read Avinash Tyagi
two pointer technique two pointers two pointer algorithm two pointers leetcode coding patterns sorted array sliding window vs two pointers 3sum two pointers coding interview patterns dsa patterns

I've been breaking down coding patterns that took me too long to internalize. The two pointer technique is the one I wish I'd studied first. It shows up in so many problems on LeetCode and in coding interviews that understanding it early would have saved me hours of brute-force attempts.

Why I kept writing nested loops

For months, my approach to array problems was consistent: try every pair with two nested loops. It worked, but it was almost always too slow. The two pointers approach existed everywhere in editorial solutions, but nobody explained the reasoning behind it. Tutorials jumped from "given a sorted array" straight to the answer.

I kept defaulting to brute force and timing out on problems that had O(n) solutions using the two pointer algorithm.

The core idea behind two pointers

The two pointer technique places two index variables at positions in your array and moves them based on conditions. The key insight is that sorting gives you directional guarantees. If your current sum is too small, moving the left pointer right makes it bigger. If too big, moving the right pointer left makes it smaller. You never guess which direction to move.

On an unsorted array, moving a pointer could increase or decrease values unpredictably. Sorting means each pointer movement eliminates an entire set of impossible combinations. That elimination is why two pointers turns O(n²) into O(n).

Two pointers technique diagram showing left and right pointers moving inward on a sorted array
Two pointers moving inward on a sorted array, eliminating impossible pairs with each step

Two Sum on a sorted array: the foundation

Given a sorted array and target sum, find two numbers that add up to the target.

two_sum.pypython
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
        else:
            right -= 1
    
    return []

Walk through it with nums = [1, 3, 5, 7, 9] and target = 8. Start with left = 0 (value 1), right = 4 (value 9). Sum is 10, too big. Move right to 3. Now sum is 8. Done in 2 checks instead of 10.

When we moved right from index 4 to 3, we skipped pairs like (3,9) and (5,9). This is safe because the array is sorted. If nums[0] + nums[4] = 10 is too big, then every other pair with nums[4] is also too big since nums[1] >= nums[0]. One move eliminates an entire column of pairs.

Three variations of two pointers

Variation 1: Opposite ends moving inward

This is the Two Sum pattern. Start at both ends, move inward based on comparisons. Container With Most Water uses this approach. You maximize the area between two heights by always moving the pointer at the shorter height.

container_water.pypython
def max_area(height):
    left, right = 0, len(height) - 1
    best = 0
    while left < right:
        width = right - left
        h = min(height[left], height[right])
        best = max(best, width * h)
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1
    return best

Variation 2: Fast and slow pointers

Both pointers start at the beginning. One moves faster. This is Floyd's cycle detection algorithm applied to arrays. Remove Duplicates from a Sorted Array demonstrates it clearly.

remove_duplicates.pypython
def remove_duplicates(nums):
    if not nums:
        return 0
    slow = 0
    for fast in range(1, len(nums)):
        if nums[fast] != nums[slow]:
            slow += 1
            nums[slow] = nums[fast]
    return slow + 1

The slow pointer advances only when a new unique value appears. The fast pointer scans ahead continuously. Their divergence tracks exactly how many duplicates exist.

Variation 3: Expanding from center

Start both pointers at one position and expand outward. This is the standard approach for palindrome two pointers problems.

palindrome.pypython
def longest_palindrome(s):
    def expand(left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return s[left + 1:right]
    
    result = ""
    for i in range(len(s)):
        odd = expand(i, i)
        even = expand(i, i + 1)
        result = max(result, odd, even, key=len)
    
    return result

Each center expansion takes O(n) worst case across n centers, giving O(n²) total. Still much better than O(n³) brute force checking every substring.

Two pointers vs sliding window

Comparison diagram showing two pointers vs sliding window techniques
Two pointers operates on sorted data with value comparisons; sliding window maintains contiguous subarrays

These two patterns both use two indices and avoid nested loops, but the intent differs. Two pointers works on sorted data with pointer movement driven by value comparisons. Sliding window maintains a contiguous subarray satisfying some property on sequential data.

Sliding window is technically a special case of two pointers where both move in the same direction and the gap represents a contiguous subarray. But the problem-solving approach differs. Sorted array with pairs means two pointers. Contiguous subarrays maintaining a property means sliding window.

I wrote about the sliding window pattern in detail here. The two patterns complement each other by covering different shapes of the same optimization.

Common mistakes with two pointers

Forgetting to sort first is the most common error. The two pointer technique depends on sorted order for directional guarantees. On unsorted data, pointer movements become unpredictable.

Not handling duplicates causes bugs in problems like 3Sum. After finding a valid triplet, advance both pointers past duplicate values.

three_sum.pypython
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

Using two pointers when a hash map is simpler wastes effort. For unsorted Two Sum where you need indices, a hash map gives O(n) without sorting. The two pointer approach works best when data is already sorted or sorting preserves the needed information.

How to identify two pointer problems

Look for these signals in problem statements: the array is sorted or can be sorted without losing information. You need pairs, triplets, or subsequences satisfying a condition. Brute force requires nested loops. Moving one pointer consistently increases or decreases a relevant value.

Practice problems for two pointers

Two Sum II (sorted input) is the foundational two pointers LeetCode problem. Explain why each pointer movement is safe to confirm understanding.

3Sum extends the two pointer technique with an outer loop. Duplicate handling is the challenge, not the pointer logic.

Container With Most Water tests the elimination argument outside a typical "find a pair" context.

Remove Duplicates from Sorted Array is the cleanest fast/slow pointer problem.

Longest Palindromic Substring practices center expansion.

Trapping Rain Water combines the two pointer algorithm with extra bookkeeping. The core logic is the same opposite-ends pattern.

After those, try the problems on Levelop's coding patterns track. That's where I worked through these variations and started seeing the connections.

Frequently Asked Questions

What is the two pointer technique in coding interviews?

The two pointer technique uses two index variables traversing a data structure simultaneously. Moving pointers based on conditions eliminates nested loops, reducing time complexity from O(n²) to O(n). It is one of the most tested patterns at Google, Amazon, and Meta.

When should I use two pointers instead of a hash map?

Use two pointers when input is sorted or sorting preserves needed information. Use a hash map when original positions matter or data cannot be meaningfully sorted. For unsorted Two Sum needing indices, a hash map works better. For sorted Two Sum or 3Sum, the two pointer approach is cleaner.

What is the difference between two pointers and sliding window?

Two pointers works on sorted data using value comparisons for pairs and triplets. Sliding window maintains contiguous subarrays satisfying properties on sequential data. Sliding window is a specialized form of two pointers where both move in the same direction, but the problem-solving mindset is different.

How do I handle duplicates in two pointer problems?

After finding a valid result, skip duplicate values by advancing the pointer while the next value equals the current one. In 3Sum, after finding a triplet, advance the left pointer past all copies and the right pointer past all copies before continuing.

Can two pointers work on unsorted arrays?

Generally no. The two pointer technique relies on sorted order for directional guarantees. On unsorted data, pointer movement gives no predictable information about whether results move toward or away from the target. Sort the array first at O(n log n) cost, or use a different technique like hash maps.

Keep reading

Coding Patterns

The Sliding Window Algorithm: The Pattern That Turns O(n²) into O(n) Overnight

I avoided the sliding window pattern for months, writing nested loops that timed out on every large test case. Here's the decision framework that finally made it click, with code templates and five practice problems in order.

Read article
Coding Patterns

Advanced Dynamic Programming: Multi-Dimensional States and Space Optimization

Learn how to solve multi-dimensional dynamic programming problems and optimize space from O(m*n) to O(n). Covers edit distance, knapsack, boolean states, and common mistakes with practical Python examples.

Read article
Coding Patterns

I Solved 3 DP Problems With the Exact Same Template. Here It Is.

Every dynamic programming problem follows the same 4 steps: define the state, find the recurrence, set the base case, determine the answer. I prove it with three LeetCode classics.

Read article