Skip to content

Two Pointers: Code Templates in 6 Languages

If you have not read the concept guide yet, start there for the intuition and the complexity analysis. This page gives you the code you can memorize and adapt during an interview. The main template runs in

O(N)
time with
O(1)
extra space, and each variation notes where its cost differs.

Main Template: Opposite Direction (Pair Search)

Two pointers start at opposite ends of a sorted array and close in on each other. Each comparison either finds the answer or rules out one end of the remaining range, which is why the loop is linear.

Use this for Two Sum II - Input Array Is Sorted .

function twoPointersPairSum(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) {
            // A sorted array only gets larger to the right,
            // so a bigger sum can only come from the left side.
            left++;
        } else {
            right--;
        }
    }
    return [-1, -1];
}
Templates only help if you can recall them without staring at the screen. Turn these snippets into flashcards for daily mobile review.

Code Breakdown

Key Variables

  • left and right: the two unsettled ends of the remaining range. Everything left of left and right of right has already been ruled out.
  • sum: the comparison value that decides which pointer moves. The decision rule is what carries the sorted-order guarantee, so keep it on one line and readable.

Visual Mechanism

    graph TD
    I["left = 0, right = n - 1"] --> C{"left < right?"}
    C -->|Yes| S["Compute arr[left] + arr[right]"]
    S --> T{"Sum vs target?"}
    T -->|"Equal"| F["Return indices"]
    T -->|"Too small"| L["left++"]
    T -->|"Too large"| R["right--"]
    L --> C
    R --> C
    C -->|No| N["Return not found"]
  

Critical Sections

The initialization pins the pointers to the two ends. Starting anywhere else would skip candidates, and the sorted guarantee only holds while the range between the pointers is the set of elements that could still form a pair.

The compare-and-move step is the heart of the template. When the sum is too small, every pair that includes arr[left] with any element to its right is also too small, because all of them use a value smaller than the current right pointer. Advancing left discards that whole group. The symmetric argument discards every pair containing arr[right] when the sum is too large. One comparison eliminates a group, not a single element, and that is where the linear bound comes from.

The termination condition left < right stops the loop before the pointers can land on the same index. With <=, a target equal to twice one element would let a single index report itself as a valid pair.

Variations

1. Same Direction (Remove Duplicates)

Use this when the input is sorted and you must rewrite it in place, keeping each unique value once. The fast pointer scans every element while slow marks where the next kept value belongs. The prefix before slow always holds the deduplicated result.

Use this for Remove Duplicates from Sorted Array .

Time

O(N)
| Space
O(1)

function removeDuplicates(nums) {
    if (nums.length === 0) return 0;

    let slow = 0; // where the next unique value will be written
    for (let fast = 1; fast < nums.length; fast++) {
        // fast found a value the kept prefix does not have yet.
        // Move the write position forward and place it there.
        if (nums[fast] !== nums[slow]) {
            slow++;
            nums[slow] = nums[fast];
        }
    }
    return slow + 1;
}

2. Fixed Pivot Plus Two Pointers (3Sum)

Use this when you need every triplet that satisfies a condition, not just one pair. Sort the array, fix one element as the pivot, then run the opposite direction pair search on the remaining range to the right of the pivot.

Use this for 3Sum .

Time

O(N^2)
| Space
O(1)
(besides the sort)

function threeSum(nums) {
    nums.sort((a, b) => a - b);
    const result = [];

    for (let i = 0; i < nums.length - 2; i++) {
        // The same pivot value produces the same triplets,
        // so skip it to keep the answer free of duplicates.
        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]]);
                // After a match, skip equal neighbors so the same
                // triplet is never recorded twice.
                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;
}
Each variation above is a separate review card in the app. Drill them individually so you do not mix up the conditions under pressure.

Now head to the practice problems to apply these templates to real interview questions.

Now that you have the templates, the next step is application. Head to the practice problems or start reviewing these templates with spaced repetition .