← All Posts
DSA Series · Arrays · Two Pointers · Opposite Ends

Opposite-End Pointers

The Core Idea

In the opposite-end pattern, one pointer starts at the beginning of the array and another starts at the end. They move toward each other based on some comparison, and the algorithm terminates when they meet or cross.

This pattern works best when the array is sorted or when the problem involves comparing elements from both extremes. The sorted order creates a monotonic relationship: if the current pair is too small, moving the left pointer right makes it bigger; if too large, moving the right pointer left makes it smaller.

Prerequisites: This page assumes you have read the Two Pointers Overview. You should understand what the two pointers technique is and why it reduces time complexity.

The General Template

int left = 0, right = n - 1;
while (left < right) {
    // Evaluate current pair
    if (condition_met(arr[left], arr[right])) {
        // Record answer
        break; // or continue searching
    } else if (need_larger_value) {
        left++;   // move left pointer right for a bigger element
    } else {
        right--;  // move right pointer left for a smaller element
    }
}

The key question for every problem is: which pointer should move, and why? The answer always comes from the sorted property giving you a guarantee about what moving a pointer does to the result.

Problem 1: Two Sum (Sorted Array)

Problem: Given a sorted array of integers and a target sum, find two numbers that add up to the target. Return their indices.

Example: Array = [2, 7, 11, 15], target = 9. Answer: indices 0 and 1 (values 2 + 7 = 9).

Why Opposite Ends Work

Since the array is sorted, the smallest element is at the left and the largest is at the right. Adding them gives a sum that we can compare to the target:

Why We Never Miss a Valid Pair

When we move left forward, we discard all pairs that include the old left pointer with any right pointer. This is safe because all those sums are even smaller (the right values are smaller or equal). Similarly, when we move right backward, we discard pairs that are too large. The monotonicity of the sorted array guarantees we never skip a valid answer.

Step-Through Animation: Two Sum

Array: [1, 3, 5, 8, 12, 15]. Target = 13. Step through to find the pair.

1 3 5 8 12 15 0 1 2 3 4 5 L R
L=0, R=5. Sum = 1+15 = 16. Click Step.

C++ Solution

vector<int> twoSum(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 {}; // no pair found
}
ApproachTimeSpace
Brute force (all pairs)O(n2)O(1)
Hash mapO(n)O(n)
Two pointers (sorted)O(n)O(1)

Problem 2: Container With Most Water

Problem: Given an array of heights, find two lines that together with the x-axis form a container that holds the most water. Return the maximum area.

Example: Heights = [1, 8, 6, 2, 5, 4, 8, 3, 7]. The maximum area is 49 (between heights 8 at index 1 and 7 at index 8, width = 7).

Why Opposite Ends Work

The area between two lines at positions left and right is:

area = min(height[left], height[right]) * (right - left)

Starting from the widest possible container (left=0, right=n-1), we want to try to find taller lines. The crucial insight:

So we always move the pointer pointing to the shorter line.

Greedy Choice

By moving the shorter side, we give up the current width but gain the chance of a taller line. Moving the taller side is provably suboptimal because the area can only decrease (same or shorter water level, narrower width).

Step-Through Animation: Container With Most Water

Heights: [2, 5, 3, 7, 4, 6]. Track the maximum area as pointers converge.

2 5 3 7 4 6 0 1 2 3 4 5 L R
L=0, R=5. Area = min(2,6)*5 = 10. Max=10. Click Step.

C++ Solution

int maxArea(vector<int>& height) {
    int left = 0, right = height.size() - 1;
    int maxWater = 0;
    while (left < right) {
        int w = right - left;
        int h = min(height[left], height[right]);
        maxWater = max(maxWater, w * h);
        if (height[left] < height[right]) {
            left++;
        } else {
            right--;
        }
    }
    return maxWater;
}

Time O(n), Space O(1)

Each pointer moves inward at most n times total. We compute the area in constant time per step. No extra data structures are needed.

Problem 3: Valid Palindrome

Problem: Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring case.

Example: "A man, a plan, a canal: Panama" is a palindrome. "race a car" is not.

Why Opposite Ends Work

A palindrome reads the same forward and backward. We compare characters starting from both ends and work inward. If every matching pair is equal, the string is a palindrome.

Step-Through Animation: Valid Palindrome

Checking if "racecar" is a palindrome.

r a c e c a r 0 1 2 3 4 5 6 L R
L=0 ('r'), R=6 ('r'). Click Step.

C++ Solution

bool isPalindrome(string s) {
    int left = 0, right = s.size() - 1;
    while (left < right) {
        while (left < right && !isalnum(s[left]))  left++;
        while (left < right && !isalnum(s[right])) right--;
        if (tolower(s[left]) != tolower(s[right])) {
            return false;
        }
        left++;
        right--;
    }
    return true;
}

When to Use Opposite-End Pointers

Look for these signals:

Common Variations

Two Sum with Duplicates

If the array has duplicates and you need all unique pairs, add skip logic after finding a pair:

if (sum == target) {
    result.push_back({left, right});
    while (left < right && nums[left] == nums[left + 1]) left++;
    while (left < right && nums[right] == nums[right - 1]) right--;
    left++; right--;
}

Pair with Given Difference

Instead of sum, find a pair with a given difference. In a sorted array, both pointers start at the left and move in the same direction. This is actually the same-direction pattern, which is covered in the next post.

Three Sum (Extension)

Fix one element with an outer loop and run two-sum on the remaining subarray. This gives O(n2) total. Covered in detail in Competition Patterns.

Practice Problems

ProblemDifficultyKey Idea
Two Sum II (LeetCode 167)MediumClassic opposite-end on sorted array
Container With Most Water (LC 11)MediumMove the shorter side inward
Valid Palindrome (LC 125)EasyCompare from both ends, skip non-alphanumeric
Squares of a Sorted Array (LC 977)EasyMerge from both ends (largest squares at extremes)
Boats to Save People (LC 881)MediumPair heaviest with lightest if possible
3Sum Closest (LC 16)MediumFix one, two-pointer on rest, track closest sum

Summary