Skip to content
beginnerPhase 1 · Foundation

Two Pointers

Master the two-pointer technique for efficient array and string traversal.

1h 15m
7 problems
Topic Progress0%

Two Pointers Introduction

What is Two Pointers?

Two pointers is a technique where you use two variables to traverse a data structure, typically from different positions.

The Core Idea

Instead of checking every pair (O(n²)), you use two pointers that move intelligently.

Visual Example

Find pair with sum = 9 in sorted array:
arr = [1, 2, 4, 5, 6, 8, 9]

Step 1:  [1, 2, 4, 5, 6, 8, 9]
          ↑                 ↑
         left             right
         sum = 1 + 9 = 10 > 9
         → move right left

Step 2:  [1, 2, 4, 5, 6, 8, 9]
          ↑              ↑
         left          right
         sum = 1 + 8 = 9 ✓ Found!

Why It Works

In a sorted array:

  • If sum is too large → move right pointer left
  • If sum is too small → move left pointer right

This eliminates half the possibilities each step.

Time Complexity

O(n) - each pointer moves at most n times.

When to Use

  1. Sorted array problems
  2. Palindrome checking
  3. Pair problems (two sum, three sum)
  4. Remove duplicates
  5. Merge sorted arrays

Two Pointer Patterns

Pattern 1: Opposite Ends

Start from both ends and move inward.

// Check if string is palindrome
boolean isPalindrome(String s) {
    int left = 0, right = s.length() - 1;
    while (left < right) {
        if (s.charAt(left) != s.charAt(right)) {
            return false;
        }
        left++;
        right--;
    }
    return true;
}

Pattern 2: Same Direction (Fast/Slow)

Both start from same position, move at different speeds.

// Remove duplicates from sorted array
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;
}

Pattern 3: Partition

Partition array based on condition.

// Move all zeros to end
void moveZeroes(int[] nums) {
    int slow = 0;
    for (int fast = 0; fast < nums.length; fast++) {
        if (nums[fast] != 0) {
            swap(nums, slow, fast);
            slow++;
        }
    }
}

Pattern 4: Two Arrays

Use pointers on two different arrays.

// Merge sorted arrays
void merge(int[] nums1, int m, int[] nums2, int n) {
    int p1 = m - 1, p2 = n - 1, p = m + n - 1;
    while (p1 >= 0 && p2 >= 0) {
        if (nums1[p1] > nums2[p2]) {
            nums1[p--] = nums1[p1--];
        } else {
            nums1[p--] = nums2[p2--];
        }
    }
    while (p2 >= 0) {
        nums1[p--] = nums2[p2--];
    }
}

Pattern 5: Three Pointers

// Three Sum
List<List<Integer>> threeSum(int[] nums) {
    Arrays.sort(nums);
    List<List<Integer>> result = new ArrayList<>();
    
    for (int i = 0; i < nums.length - 2; i++) {
        if (i > 0 && nums[i] == nums[i - 1]) continue;  // skip duplicates
        
        int left = i + 1, right = nums.length - 1;
        while (left < right) {
            int sum = nums[i] + nums[left] + nums[right];
            if (sum == 0) {
                result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                while (left < right && nums[left] == nums[left + 1]) left++;
                while (left < right && nums[right] == nums[right - 1]) right--;
                left++;
                right--;
            } else if (sum < 0) {
                left++;
            } else {
                right--;
            }
        }
    }
    return result;
}

Classic Two Pointer Problems

1. Valid Palindrome (LeetCode 125)

public boolean isPalindrome(String s) {
    int left = 0, right = s.length() - 1;
    while (left < right) {
        while (left < right && !Character.isLetterOrDigit(s.charAt(left))) left++;
        while (left < right && !Character.isLetterOrDigit(s.charAt(right))) right--;
        if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
            return false;
        }
        left++;
        right--;
    }
    return true;
}
// Time: O(n), Space: O(1)

2. Two Sum II (LeetCode 167)

public int[] twoSum(int[] numbers, int target) {
    int left = 0, right = numbers.length - 1;
    while (left < right) {
        int sum = numbers[left] + numbers[right];
        if (sum == target) {
            return new int[] {left + 1, right + 1};
        } else if (sum < target) {
            left++;
        } else {
            right--;
        }
    }
    return new int[] {};
}
// Time: O(n), Space: O(1)

3. Container With Most Water (LeetCode 11)

public int maxArea(int[] height) {
    int left = 0, right = height.length - 1;
    int maxWater = 0;
    while (left < right) {
        int water = Math.min(height[left], height[right]) * (right - left);
        maxWater = Math.max(maxWater, water);
        if (height[left] < height[right]) {
            left++;
        } else {
            right--;
        }
    }
    return maxWater;
}
// Time: O(n), Space: O(1)

4. Trapping Rain Water (LeetCode 42)

public int trap(int[] height) {
    int left = 0, right = height.length - 1;
    int leftMax = 0, rightMax = 0;
    int water = 0;
    while (left < right) {
        if (height[left] < height[right]) {
            if (height[left] >= leftMax) {
                leftMax = height[left];
            } else {
                water += leftMax - height[left];
            }
            left++;
        } else {
            if (height[right] >= rightMax) {
                rightMax = height[right];
            } else {
                water += rightMax - height[right];
            }
            right--;
        }
    }
    return water;
}
// Time: O(n), Space: O(1)

5. Remove Duplicates (LeetCode 26)

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;
}
// Time: O(n), Space: O(1)

Edge Cases and Tips

Common Edge Cases

  1. Empty array
if (nums.length == 0) return 0;
  1. Single element
if (nums.length == 1) return specialValue;
  1. All same elements
// For duplicate removal, this should return 1
  1. No valid pair exists
return new int[] {};  // or -1, or false
  1. Integer overflow
// Use long for sums: long sum = (long)nums[left] + nums[right];

Tips for Success

  1. Identify the pattern

    • Sorted array? → Opposite ends
    • Remove duplicates? → Fast/Slow
    • Merge? → Two arrays
  2. Handle duplicates explicitly

while (left < right && nums[left] == nums[left + 1]) left++;
  1. Check bounds
while (left < right && left < nums.length && right >= 0)
  1. Use descriptive names
int left = 0;          // not 'i'
int right = n - 1;     // not 'j'

Two Pointers vs Other Techniques

Situation Use
Sorted array, find pair Two Pointers
Unsorted array, find pair HashMap
Subarray with condition Sliding Window
All pairs needed Nested Loops
Palindrome check Two Pointers

Interactive Visualization

Two Pointers Visualization

Press Play or Step to begin
CurrentFound / DoneEliminatedUnvisited

Practice Problems

0/5solved
Valid Palindrome
Two Pointers

Check if a string is a palindrome after removing non-alphanumeric characters.

Example:

Input: s = "A man, a plan, a canal: Panama"

Output: true

"amanaplanacanalpanama" is a palindrome.

Optimal Solution — O(n) time, O(1) space

Two pointers from both ends

class Solution {
    public boolean isPalindrome(String s) {
        int left = 0, right = s.length() - 1;
        while (left < right) {
            while (left < right && !Character.isLetterOrDigit(s.charAt(left))) left++;
            while (left < right && !Character.isLetterOrDigit(s.charAt(right))) right--;
            if (Character.toLowerCase(s.charAt(left)) != Character.toLowerCase(s.charAt(right))) {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
}

Edge Cases:

  • Empty string
  • Single character
  • All non-alphanumeric
Container With Most Water
Two Pointers

Find two lines that together with the x-axis form a container that holds the most water.

Example:

Input: height = [1,8,6,2,5,4,8,3,7]

Output: 49

Maximum area is between lines at index 1 and 8.

Optimal Solution — O(n) time, O(1) space

Two pointers, move the shorter line

class Solution {
    public int maxArea(int[] height) {
        int left = 0, right = height.length - 1;
        int maxWater = 0;
        while (left < right) {
            int water = Math.min(height[left], height[right]) * (right - left);
            maxWater = Math.max(maxWater, water);
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        return maxWater;
    }
}

Edge Cases:

  • All same heights
  • Strictly increasing
  • Strictly decreasing
Three Sum
Two Pointers + Sort

Given an integer array nums, return all unique triplets [nums[i], nums[j], nums[k]] such that nums[i] + nums[j] + nums[k] == 0.

Example:

Input: nums = [-1,0,1,2,-1,-4]

Output: [[-1,-1,2],[-1,0,1]]

Two unique triplets sum to zero.

Optimal Solution — O(n^2) time, O(1) space

Sort, fix one element, two pointers on rest

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList<>();
        for (int i = 0; i < nums.length - 2; i++) {
            if (i > 0 && nums[i] == nums[i-1]) continue;
            int left = i + 1, right = nums.length - 1;
            while (left < right) {
                int sum = nums[i] + nums[left] + nums[right];
                if (sum == 0) {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    while (left < right && nums[left] == nums[left+1]) left++;
                    while (left < right && nums[right] == nums[right-1]) right--;
                    left++; right--;
                } else if (sum < 0) left++;
                else right--;
            }
        }
        return result;
    }
}

Edge Cases:

  • All zeros
  • No valid triplet
  • All same elements
Trapping Rain Water
Two Pointers

Given n non-negative integers representing an elevation map, compute how much water it can trap after raining.

Example:

Input: height = [0,1,0,2,1,0,1,3,2,1,2,1]

Output: 6

6 units of water can be trapped.

Optimal Solution — O(n) time, O(1) space

Two pointers tracking leftMax and rightMax

class Solution {
    public int trap(int[] height) {
        int left = 0, right = height.length - 1;
        int leftMax = 0, rightMax = 0, water = 0;
        while (left < right) {
            if (height[left] < height[right]) {
                if (height[left] >= leftMax) leftMax = height[left];
                else water += leftMax - height[left];
                left++;
            } else {
                if (height[right] >= rightMax) rightMax = height[right];
                else water += rightMax - height[right];
                right--;
            }
        }
        return water;
    }
}

Edge Cases:

  • No water can be trapped
  • All same heights
  • Strictly increasing then decreasing
Valid Palindrome II
Two Pointers with Skip

Given a string s, return true if s is a palindrome, or false otherwise. You can delete at most one character.

Example:

Input: s = "abca"

Output: true

Delete 'c' to get palindrome.

Optimal Solution — O(n) time, O(1) space

Two pointers, skip mismatch once

class Solution {
    public boolean validPalindrome(String s) {
        int left = 0, right = s.length() - 1;
        while (left < right) {
            if (s.charAt(left) != s.charAt(right)) {
                return isPalin(s, left + 1, right) || isPalin(s, left, right - 1);
            }
            left++; right--;
        }
        return true;
    }
    private boolean isPalin(String s, int l, int r) {
        while (l < r) {
            if (s.charAt(l++) != s.charAt(r--)) return false;
        }
        return true;
    }
}

Edge Cases:

  • Already palindrome
  • Single character
  • Cannot become palindrome

Quiz

1. When should you use two pointers instead of nested loops?

Question 1 options

2. What is the time complexity of the Two Sum II solution?

Question 2 options

3. In Container With Most Water, why do we move the shorter line?

Question 3 options

4. What is the primary purpose of Two Pointers?

Question 4 options

Flashcards

Question

What are the two main two-pointer patterns?

Answer

1) Opposite ends (start from both sides), 2) Same direction (fast/slow pointers)

Question

When can two pointers reduce O(n²) to O(n)?

Answer

When the array is sorted and you can eliminate half the search space by moving pointers.

Question

How do you handle duplicates in Two Sum?

Answer

Skip duplicate values: while (left < right && nums[left] == nums[left+1]) left++;

Question

What is Two Pointers?

Answer

Two Pointers is a key concept in software engineering.

Question

When to use Two Pointers?

Answer

Use Two Pointers when building production systems that require reliability, scalability, and maintainability.

Revision Notes

Key Takeaways

  • 1.Two pointers reduces O(n²) to O(n) on sorted arrays
  • 2.Opposite ends for palindrome and pair problems
  • 3.Fast/slow for cycle detection and duplicate removal
  • 4.Always handle duplicates explicitly
  • 5.Check for integer overflow with large numbers

Interview Tips

  • Ask if array is sorted before suggesting two pointers
  • Explain why moving each pointer is safe
  • Discuss time and space complexity
  • Handle edge cases explicitly

Cheat Sheet

Two Pointers Cheat Sheet

Patterns:

  1. Opposite Ends: left=0, right=n-1
  2. Fast/Slow: both start at 0, different speeds
  3. Partition: separate elements by condition

When to Use:

  • Sorted array
  • Palindrome check
  • Pair problems
  • Remove duplicates

Time: O(n) - each pointer moves at most n times
Space: O(1) - only two variables

Edge Cases:

  • Empty array
  • Single element
  • No valid pair
  • Integer overflow