Two Pointers

The pattern: Use two index variables that move through the data structure — either toward each other (opposite ends) or in the same direction — to solve the problem in a single pass without extra space.

Why this matters in interviews: Two pointers is the go-to optimization for sorted array problems. It replaces hash maps (O(n) space) or nested loops (O(n²) time) with an elegant O(n) time, O(1) space solution.


When to Recognize It


How It Works

Imagine two people walking toward each other on a bridge. One starts at the left end, one at the right. Based on what they see (too big? too small?), one of them takes a step inward. They meet somewhere in the middle — and by then, they’ve checked every useful combination.

flowchart LR
    L["left = 0"]:::client
    A["sorted array"]:::service
    R["right = n-1"]:::data
    
    L -->|"move right if sum too small"| A
    A -->|"move left if sum too big"| R

    classDef client fill:#4c3a5e,stroke:#818cf8,color:#e2e8f0
    classDef service fill:#1a3a2a,stroke:#4ade80,color:#e2e8f0
    classDef data fill:#3b3520,stroke:#fbbf24,color:#e2e8f0

Why it works on sorted arrays: If arr[left] + arr[right] < target, moving left forward increases the sum (since the array is sorted). If the sum is too big, moving right backward decreases it. Each step eliminates an entire row or column of the search space.


Template Code

Code

def two_sum_sorted(nums, target):
    """Find pair in sorted array that sums to 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   # need bigger sum
        else:
            right -= 1  # need smaller sum

    return []  # no pair found
int[] twoSumSorted(int[] nums, int target) {
    int left = 0, right = nums.length - 1;

    while (left < right) {
        int sum = nums[left] + nums[right];
        if (sum == target) return new int[]{left, right};
        else if (sum < target) left++;
        else right--;
    }
    return new int[]{};
}
vector<int> twoSumSorted(vector<int>& nums, int target) {
    int left = 0, right = nums.size() - 1;

    while (left < right) {
        int sum = nums[left] + nums[right];
        if (sum == target) return {left, right};
        else if (sum < target) left++;
        else right--;
    }
    return {};
}
function twoSumSorted(nums, target) {
    let left = 0, right = nums.length - 1;

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

Variations

Three Sum (Sort + Two Pointers)

Fix one element, then use two pointers on the remaining sorted subarray. Skip duplicates to avoid repeated triplets.

Code

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

    for i in range(len(nums) - 2):
        if i > 0 and nums[i] == nums[i - 1]:
            continue  # skip duplicates

        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
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;
}
vector<vector<int>> threeSum(vector<int>& nums) {
    sort(nums.begin(), nums.end());
    vector<vector<int>> result;

    for (int i = 0; i < (int)nums.size() - 2; i++) {
        if (i > 0 && nums[i] == nums[i - 1]) continue;
        int left = i + 1, right = nums.size() - 1;
        while (left < right) {
            int sum = nums[i] + nums[left] + nums[right];
            if (sum == 0) {
                result.push_back({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;
}
function threeSum(nums) {
    nums.sort((a, b) => a - b);
    const result = [];

    for (let i = 0; i < nums.length - 2; i++) {
        if (i > 0 && nums[i] === nums[i - 1]) continue;
        let left = i + 1, right = nums.length - 1;
        while (left < right) {
            const sum = nums[i] + nums[left] + nums[right];
            if (sum === 0) {
                result.push([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;
}

Container With Most Water (Max Area)

Two pointers start at opposite ends. Move the shorter wall inward — keeping the taller wall gives you a better chance of finding a larger area.

Same-Direction Pointers (Fast and Slow)

Used for removing duplicates in-place, partitioning, or detecting cycles. The slow pointer marks the “write position” while the fast pointer scans ahead.


Complexity

Variant Time Space
Two Sum (sorted) O(n) O(1)
Three Sum O(n²) O(1) extra
Container With Most Water O(n) O(1)
Trapping Rain Water O(n) O(1)

Common Mistakes


Practice Problems


Key Takeaways