Learn/DSA/Two Pointers
AlgorithmsBeginner9 min

Two Pointers

Two indices sweeping an array in coordination turn O(n²) brute force into a single O(n) pass.

Two PointersArraysSorted Input

The two-pointer pattern is the art of walking an array with two indices instead of one. Rather than re-scanning from scratch (the O(n²) trap), you keep two markers and move them toward each other or in tandem, making a decision at each step that lets you skip whole regions of the input.

The trigger to reach for it: the input is sorted (or you can sort it), and you are hunting for a pair, a window, or a partition. Sortedness is the magic ingredient — it means moving a pointer one way always increases some quantity and the other way always decreases it, so you can steer toward a target without guessing.

Array
5
0
8
1
12
2
3
3
9
4
21
5
accessed — O(1)shifting — O(n)
Access is O(1); insert/delete shift elements → O(n).
Left and right pointers start at the ends of a sorted array and converge — each comparison rules out one entire side.

Opposite-ends: two-sum on a sorted array

Place one pointer at the start (l) and one at the end (r). Their sum is the largest possible for that left value. If the sum is too big, the only way to shrink it is to move r left; if too small, move l right. Each move eliminates a whole column of pairs, so the whole search is a single O(n) sweep.

Two-sum on a sorted array — O(n) time, O(1) space
function twoSumSorted(nums, target) {
  let l = 0, r = nums.length - 1
  while (l < r) {
    const sum = nums[l] + nums[r]
    if (sum === target) return [l, r]
    if (sum < target) l++   // need a bigger sum
    else r--                // need a smaller sum
  }
  return [-1, -1]
}

Why sorted matters

On an unsorted array, moving a pointer tells you nothing — the sum could go up or down. On a sorted one, direction is guaranteed. That monotonicity is what makes the O(n) sweep correct. No sortedness, no two pointers (reach for a hash set instead).

Container with most water

Given heights, the area between lines l and r is min(h[l], h[r]) × (r − l). Start at the widest span and step inward. The width only shrinks, so the only hope of a bigger area is a taller line — so always move the shorter wall inward. Moving the taller one could never help, which is exactly why one pass suffices.

Container with most water — O(n)
function maxArea(height) {
  let l = 0, r = height.length - 1, best = 0
  while (l < r) {
    const area = Math.min(height[l], height[r]) * (r - l)
    best = Math.max(best, area)
    if (height[l] < height[r]) l++  // move the shorter wall
    else r--
  }
  return best
}

Fast & slow: detecting a cycle

The pointers do not have to start at opposite ends. In Floyd's tortoise-and-hare, a slow pointer advances one step while a fast one advances two. If there is a loop, the fast pointer laps the slow one and they collide; if the fast pointer runs off the end, there is no loop. This same "one step vs. two" trick also finds the middle of a list in one pass.

Cycle detection — fast & slow pointers
function hasCycle(head) {
  let slow = head, fast = head
  while (fast && fast.next) {
    slow = slow.next        // +1
    fast = fast.next.next   // +2
    if (slow === fast) return true  // they met -> loop
  }
  return false
}

Partition: read & write pointers

A third flavour uses a slow write pointer trailing a fast read pointer. The read pointer scans everything; the write pointer only advances when it finds an element worth keeping. This is how you remove duplicates in place, move zeros to the end, or run the partition step of quicksort — all in O(n) time and O(1) space.

  • Opposite ends — sorted pairs, palindromes, container-with-most-water. Converge inward.
  • Fast & slow — cycle detection, finding the middle, nth-from-end.
  • Read & write — in-place filtering, dedup, partition.
  • Precondition check: is the array sorted, or does the problem mention a pair or window? That is your cue.
  • Payoff: O(n²) brute force becomes O(n) time, O(1) space.

Next up

When the two pointers stay on the same side and bound a contiguous stretch, they become a sliding window — the next lesson.

Section navigation