Algorithm Guide12 min readNov 28, 2025

The Two Pointer Technique: A Complete Guide

Master the two pointer pattern used in 100+ LeetCode problems. Learn when to use it, common variations, and solve problems faster.

Introduction

The two pointer technique is one of the most fundamental algorithmic patterns you'll encounter in coding interviews. It's elegant, efficient, and once you master it, you'll recognize opportunities to apply it everywhere.

At its core, the two pointer technique involves using two variables (pointers) to traverse a data structure—typically an array or string—in a coordinated way. Instead of using nested loops (O(n²)), two pointers often reduce the time complexity to O(n).

Why Two Pointers Matter

Consider this: if you're asked to find a pair of numbers in a sorted array that sum to a target, a naive approach would check every possible pair:

# Naive approach - O(n²)
def find_pair(arr, target):
    for i in range(len(arr)):
        for j in range(i + 1, len(arr)):
            if arr[i] + arr[j] == target:
                return [i, j]
    return []

With two pointers, we can solve this in O(n):

# Two pointer approach - O(n)
def find_pair(arr, target):
    left, right = 0, len(arr) - 1
    while left < right:
        current_sum = arr[left] + arr[right]
        if current_sum == target:
            return [left, right]
        elif current_sum < target:
            left += 1
        else:
            right -= 1
    return []

When to Use Two Pointers

Look for these signals that suggest a two pointer solution:

  1. Sorted array or string* - The most common indicator
  2. Finding pairs or triplets* with a specific sum or property
  3. Comparing elements from both ends* (palindromes, reversing)
  4. In-place modifications* without extra space
  5. Merging sorted arrays* or linked lists
  6. Removing duplicates* from sorted data
  7. Partitioning* elements based on a condition

The Key Insight

  • Two pointers work because they systematically eliminate possibilities. In a sorted array looking for a target sum:
  • If the current sum is too small, moving the left pointer right increases it
  • If the current sum is too large, moving the right pointer left decreases it
  • Each move eliminates one possibility, guaranteeing we check all valid pairs

Pattern 1: Opposite Direction (Start & End)

This is the most common pattern. Pointers start at opposite ends and move toward each other.

Template

def opposite_direction(arr):
    left, right = 0, len(arr) - 1
    
    while left < right:
        # Process current pair
        if condition_met(arr[left], arr[right]):
            return result
        
        # Move pointers based on comparison
        if should_move_left(arr[left], arr[right]):
            left += 1
        else:
            right -= 1
    
    return default_result

Example: Container With Most Water

Given heights of vertical lines, find two lines that form a container holding the most water.

def max_area(height):
    left, right = 0, len(height) - 1
    max_water = 0
    
    while left < right:
        # Calculate current area
        width = right - left
        h = min(height[left], height[right])
        max_water = max(max_water, width * h)
        
        # Move the shorter line inward
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1
    
    return max_water

Why this works: The width decreases as pointers move inward, so we can only increase area by finding a taller line. Moving the taller line can never increase the area (height is limited by the shorter line), so we always move the shorter one.

Pattern 2: Same Direction (Fast & Slow)

Both pointers start at the same position but move at different speeds or under different conditions.

Template

def same_direction(arr):
    slow = 0
    
    for fast in range(len(arr)):
        if should_include(arr[fast]):
            arr[slow] = arr[fast]
            slow += 1
    
    return slow  # Length of processed portion

Example: Remove Duplicates from Sorted Array

Remove duplicates in-place and return the new length.

def remove_duplicates(nums):
    if not nums:
        return 0
    
    slow = 1  # Position for next unique element
    
    for fast in range(1, len(nums)):
        if nums[fast] != nums[fast - 1]:
            nums[slow] = nums[fast]
            slow += 1
    
    return slow

Example: Move Zeroes

Move all zeros to the end while maintaining order of non-zero elements.

def move_zeroes(nums):
    slow = 0  # Position for next non-zero
    
    for fast in range(len(nums)):
        if nums[fast] != 0:
            nums[slow], nums[fast] = nums[fast], nums[slow]
            slow += 1

Pattern 3: Two Arrays

Use one pointer for each array, useful for merging or comparing sorted arrays.

Example: Merge Sorted Arrays

def merge(nums1, m, nums2, n):
    # Start from the end to avoid overwriting
    p1, p2, p = m - 1, n - 1, m + n - 1
    
    while p1 >= 0 and p2 >= 0:
        if nums1[p1] > nums2[p2]:
            nums1[p] = nums1[p1]
            p1 -= 1
        else:
            nums1[p] = nums2[p2]
            p2 -= 1
        p -= 1
    
    # Copy remaining elements from nums2
    nums1[:p2 + 1] = nums2[:p2 + 1]

Common Mistakes to Avoid

1. Off-by-One Errors Always clarify whether to use `left < right` or `left <= right`: - Use `<` when looking for pairs (different indices) - Use `<=` when processing single elements

2. Infinite Loops Ensure at least one pointer moves in each iteration:

# BAD - potential infinite loop
while left < right:
    if condition:
        # Do something but don't move pointers!
        pass

# GOOD - always move at least one pointer
while left < right:
    if condition:
        result = process(left, right)
    left += 1  # or right -= 1

3. Forgetting the Sorted Requirement Two pointers with opposite direction typically requires sorted input. If unsorted, either: - Sort first (adds O(n log n)) - Use a different approach (hash map, etc.)

4. Not Handling Edge Cases - Empty array - Single element - All elements are the same - No valid pairs exist

Practice Problems

Start with these problems to build your two pointer intuition:

  1. Beginner:*
  2. Two Sum II (sorted array)
  3. Valid Palindrome
  4. Remove Duplicates from Sorted Array
  1. Intermediate:*
  2. 3Sum
  3. Container With Most Water
  4. Trapping Rain Water
  1. Advanced:*
  2. 4Sum
  3. Minimum Window Substring (with sliding window)

Key Takeaways

  1. Recognize the pattern:* Sorted arrays, pair/triplet finding, in-place modifications
  2. Choose the right variant:* Opposite direction vs. same direction vs. two arrays
  3. Think about invariants:* What property do your pointers maintain?
  4. Practice the movement logic:* When should each pointer move?
  5. Handle edge cases:* Empty inputs, single elements, no solution

The two pointer technique is foundational—master it, and you'll have a powerful tool for dozens of interview problems.

Recommended next

Practice These Problems

💡 Ask me anything about coding!