Two Pointers

Two Pointers

The Two Pointers pattern transforms

O(N^2)
brute force solutions into elegant
O(N)
linear scans. By using two indices that move through data strategically, you can solve a wide range of array and string problems with minimal space overhead.

Real-World Analogy

Imagine two people reading the same book to check if it reads the same forwards and backwards (a palindrome):

  • One person starts at the first page, another at the last
  • They each read their current page and compare
  • If pages match, both move one page toward the middle
  • If they meet in the middle without mismatches, it is a palindrome

This is exactly how the two pointers technique works - two positions moving through data based on comparisons, converging toward a solution.

Visual Explanation

Here is how two pointers find a pair summing to 9 in a sorted array:

  flowchart LR
    subgraph Step1["Step 1: Sum = 1+8 = 9"]
        A1["[1]"] --> B1["2"] --> C1["3"] --> D1["5"] --> E1["[8]"]
    end

    subgraph Step2["Found: indices 0 and 4"]
        A2["1 + 8 = 9"]
    end

    style A1 fill:#c8e6c9
    style E1 fill:#c8e6c9

The pointers start at opposite ends. Since 1 + 8 = 9 equals our target, we found the answer immediately.

Movement Logic

  flowchart TD
    Start[Initialize left=0, right=n-1] --> Compare{sum vs target?}
    Compare -->|sum == target| Found[Return indices]
    Compare -->|sum < target| MoveLeft[left++]
    Compare -->|sum > target| MoveRight[right--]
    MoveLeft --> Check{left < right?}
    MoveRight --> Check
    Check -->|Yes| Compare
    Check -->|No| NotFound[No solution]

When to Use This Pattern

Use Two Pointers when you see:

  • Input array is sorted or can be sorted
  • Need to find pairs with a specific sum or difference
  • Problem asks for in-place array modification
  • Checking palindromes or string symmetry
  • Comparing elements from both ends
  • Merging two sorted arrays
  • Removing duplicates from sorted data

Core Implementation

// Opposite Direction: Find pair with target sum
function twoSum(arr, target) {
    let left = 0;
    let right = arr.length - 1;

    while (left < right) {
        const sum = arr[left] + arr[right];
        if (sum === target) {
            return [left, right];
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    return [-1, -1];
}

// Same Direction: Remove duplicates
function removeDuplicates(nums) {
    if (nums.length === 0) return 0;
    let slow = 0;

    for (let fast = 1; fast < nums.length; fast++) {
        if (nums[fast] !== nums[slow]) {
            slow++;
            nums[slow] = nums[fast];
        }
    }
    return slow + 1;
}
# Opposite Direction: Find pair with target sum
def two_sum(arr: list, target: int) -> list:
    left, right = 0, len(arr) - 1

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

# Same Direction: Remove duplicates
def remove_duplicates(nums: list) -> int:
    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
// Opposite Direction: Find pair with target sum
public int[] twoSum(int[] arr, int target) {
    int left = 0, right = arr.length - 1;

    while (left < right) {
        int sum = arr[left] + arr[right];
        if (sum == target) {
            return new int[]{left, right};
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    return new int[]{-1, -1};
}

// Same Direction: Remove duplicates
public int removeDuplicates(int[] nums) {
    if (nums.length == 0) return 0;
    int slow = 0;

    for (int fast = 1; fast < nums.length; fast++) {
        if (nums[fast] != nums[slow]) {
            slow++;
            nums[slow] = nums[fast];
        }
    }
    return slow + 1;
}
// Opposite Direction: Find pair with target sum
vector<int> twoSum(vector<int>& arr, int target) {
    int left = 0, right = arr.size() - 1;

    while (left < right) {
        int sum = arr[left] + arr[right];
        if (sum == target) {
            return {left, right};
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    return {-1, -1};
}

// Same Direction: Remove duplicates
int removeDuplicates(vector<int>& nums) {
    if (nums.empty()) return 0;
    int slow = 0;

    for (int fast = 1; fast < nums.size(); fast++) {
        if (nums[fast] != nums[slow]) {
            slow++;
            nums[slow] = nums[fast];
        }
    }
    return slow + 1;
}
// Opposite Direction: Find pair with target sum
func twoSum(arr []int, target int) []int {
    left, right := 0, len(arr)-1

    for left < right {
        sum := arr[left] + arr[right]
        if sum == target {
            return []int{left, right}
        } else if sum < target {
            left++
        } else {
            right--
        }
    }
    return []int{-1, -1}
}

// Same Direction: Remove duplicates
func removeDuplicates(nums []int) int {
    if len(nums) == 0 { return 0 }
    slow := 0

    for fast := 1; fast < len(nums); fast++ {
        if nums[fast] != nums[slow] {
            slow++
            nums[slow] = nums[fast]
        }
    }
    return slow + 1
}
# Opposite Direction: Find pair with target sum
def two_sum(arr, target)
  left, right = 0, arr.length - 1

  while left < right
    sum = arr[left] + arr[right]
    if sum == target
      return [left, right]
    elsif sum < target
      left += 1
    else
      right -= 1
    end
  end
  [-1, -1]
end

# Same Direction: Remove duplicates
def remove_duplicates(nums)
  return 0 if nums.empty?
  slow = 0

  (1...nums.length).each do |fast|
    if nums[fast] != nums[slow]
      slow += 1
      nums[slow] = nums[fast]
    end
  end
  slow + 1
end

Complexity Analysis

VariationTimeSpaceUse Case
Opposite Direction
O(N)
O(1)
Sum pairs, palindromes
Same Direction
O(N)
O(1)
Remove duplicates
Three Pointers
O(N^2)
O(1)
3Sum problems

Why O(N)? Each pointer moves at most N times, and they never move backwards once they have advanced. The total work is proportional to the array length.

Common Mistakes

  1. Infinite loops: Forgetting to move pointers when condition is met
  2. Off-by-one errors: Using <= instead of < in while condition
  3. Not handling duplicates: Missing skip logic for duplicate values in 3Sum
  4. Wrong pointer movement: Moving the wrong pointer based on comparison

Next Steps

Ready to implement? Check out our Code Templates for reusable patterns.

Want to practice? Head to Practice Problems for curated LeetCode problems.

Pro Tip: Always draw the array and trace pointer movements on paper before coding. This catches edge cases early and clarifies your logic.