The two pointer technique

Turn a nested loop into a single pass by walking the array from both ends, or at two speeds.

Two pointers replaces "try every pair" with "move the pointer that can improve the answer". It is the single highest-value array pattern, and it almost always needs sorted input.

Converging pointers

Find two numbers in a sorted array that sum to a target:

function pairSum(sorted, target) {
  let lo = 0;
  let hi = sorted.length - 1;

  while (lo < hi) {
    const sum = sorted[lo] + sorted[hi];

    if (sum === target) return [lo, hi];
    if (sum < target) lo++;      // need more: only the left can grow
    else hi--;                   // need less: only the right can shrink
  }

  return null;
}

O(n) time, O(1) space, against O(n²) for the nested loop. The correctness argument is worth learning to say out loud: if the sum is too small, no pair using lo can ever work, because hi is already the largest remaining partner — so discarding lo discards nothing.

Same-direction pointers

A read pointer and a write pointer compact an array in place. Removing duplicates from a sorted array:

function dedupe(sorted) {
  if (sorted.length === 0) return 0;

  let write = 1;
  for (let read = 1; read < sorted.length; read++) {
    if (sorted[read] !== sorted[write - 1]) sorted[write++] = sorted[read];
  }

  return write;   // new logical length
}

Fast and slow pointers

One pointer moves two steps, the other one. This finds the middle of a sequence in one pass and detects cycles — the technique the linked list chapter builds on.

When to reach for it

  • The array is sorted, or sorting it is affordable.
  • You are looking for a pair or triple that satisfies a condition.
  • You are partitioning, reversing, or compacting in place.
  • The problem says "in O(1) extra space".

For three-sum, sort, fix the first element, then run two pointers on the rest: O(n²) total, and the standard expected answer.