Articles

The Two Pointers Technique Explained

Two pointers walk a sorted array or string from both ends (or in tandem) to solve problems in one linear pass instead of nested loops.

The Lycoris Team The Lycoris Team · · 5 min read
Chalkboard covered in diagrams and equations

The two pointers technique solves array and string problems by tracking two indices that move through the data in a coordinated way, instead of comparing every element against every other element. Where a naive approach nests one loop inside another and costs O(n²), two pointers typically finish the same problem in a single O(n) pass.

The core idea

Most two-pointer problems fall into one of two shapes:

  • Opposite ends, closing inward. One pointer starts at index 0, the other at the last index. Each step moves one or both pointers toward the middle based on a comparison, until they meet.
  • Same direction, different speeds. Both pointers start near the beginning; a “slow” pointer and a “fast” pointer advance at different rates, or one waits while the other scans ahead.

Either way, the trick is that once you’ve established an invariant — usually “the array is sorted” or “everything before the slow pointer satisfies some condition” — you never need to re-examine positions you’ve already ruled out. That’s what collapses the nested loop into a single pass.

Classic example: pair sum in a sorted array

Given a sorted array, find two numbers that add up to a target value. The brute-force approach checks every pair with two nested loops. With two pointers:

  1. Set left = 0 and right = length - 1.
  2. Compute sum = arr[left] + arr[right].
  3. If sum equals the target, you’re done.
  4. If sum is too small, increment left (you need a bigger value).
  5. If sum is too large, decrement right (you need a smaller value).
  6. Repeat until the pointers meet.

Because the array is sorted, moving left forward only ever increases the sum, and moving right back only ever decreases it. That monotonic property is what guarantees correctness in one pass — every element is visited at most once by each pointer.

Same-direction pointers: removing duplicates in place

A second common pattern uses both pointers moving forward at different rates. To remove duplicates from a sorted array in place: a write pointer tracks where the next unique value should go, while a read pointer scans ahead through the whole array. Whenever read finds a value different from the last one written, it’s copied to write, and write advances. By the time read reaches the end, everything before write is the deduplicated result — no extra array allocated.

This same slow/fast shape shows up in linked-list cycle detection, where a slow pointer advances one node at a time and a fast pointer advances two — if they ever meet, there’s a cycle.

A three-pointer variant: partitioning

Some problems need more than two positions tracked at once. A classic example is the “Dutch national flag” partitioning problem: given an array of three distinct values, rearrange it so all instances of the first value come before the second, which come before the third — in one pass, without extra memory. The solution uses three pointers: low marks the boundary of the first group, high marks the boundary of the third group from the end, and mid scans through the middle, swapping elements toward low or high as it encounters them and only advancing past a position once it’s settled into its final group. This is the same technique quicksort uses during its partition step when it needs to group elements into “less than,” “equal to,” and “greater than” the pivot.

Two pointers vs the sliding window technique

These two techniques are often confused because both use a pair of indices, but they solve different problems. The sliding window technique maintains a contiguous range between two pointers and grows or shrinks that range to track a running property (a sum, a count of distinct characters, and so on). Two pointers, by contrast, doesn’t necessarily track a range at all — in the pair-sum example above, left and right aren’t the boundaries of a subarray you care about, they’re just two independent search positions closing in on each other.

Two pointersSliding window
What the pointers representTwo independent search positionsThe two ends of one active range
Typical directionOften opposite ends, closing inwardBoth move forward; the gap resizes
Common use caseSorted-array search, partitioningSubstrings, subarrays, running aggregates
ComplexityO(n)O(n)

In practice, a sliding window problem is a special case where the “pointer positions” happen to be the boundaries of a window you’re actively maintaining.

When two pointers applies

The technique works whenever there’s an ordering or monotonic property to exploit — most often a sorted array, but also linked lists (cycle detection, finding the middle node), string palindrome checks (compare characters from both ends inward), and partitioning schemes like the ones used in quicksort to split an array around a pivot. If the data isn’t sorted and there’s no monotonic relationship to lean on, two pointers usually doesn’t apply directly — you’d need to sort first (paying an O(n log n) cost upfront) or reach for a different structure, like a hash table for O(1) lookups instead.

It’s a frequent subject in coding interviews precisely because it’s easy to state and easy to get subtly wrong — off-by-one errors at the pointer boundaries, or forgetting to handle duplicate values, are the usual bugs. Tracing through a small example by hand before coding it is the fastest way to catch those.

The takeaway

Two pointers turns O(n²) nested-loop problems into O(n) single-pass ones by exploiting an ordering property — usually sortedness — so that advancing one pointer never requires revisiting positions the other has already ruled out. It comes in two flavors: opposite-end pointers that close inward, and same-direction pointers moving at different rates. When you spot a sorted array, a palindrome check, or a linked-list traversal problem, two pointers is worth trying before reaching for nested loops or extra memory.

The Lycoris Team The Lycoris Team · · 4 min read

The KMP Algorithm: Fast String Matching Explained

The Knuth-Morris-Pratt algorithm finds a pattern inside a text in linear time by never re-examining characters it has already matched.

#Computer Science #Algorithms #Data Structures
The Lycoris Team The Lycoris Team · · 5 min read

What Is a Ring Buffer?

A ring buffer is a fixed-size array that wraps its read and write pointers around, giving O(1) enqueue and dequeue without ever resizing.

#Computer Science #Algorithms #Data Structures
The Lycoris Team The Lycoris Team · · 5 min read

Fenwick Trees (Binary Indexed Trees), Explained

A Fenwick tree, or binary indexed tree, answers prefix-sum queries and point updates in O(log n) with far less memory than a segment tree.

#Computer Science #Algorithms #Data Structures