Two Pointers
The Intuition
Two pointers comes in two flavors that share one principle: at every step, the algorithm rules out a chunk of the search space in O(1) work, never reconsidering it.
Opposite-direction (converging) pointers. Used on sorted arrays for pair-sum and similar problems. Place a pointer at index 0 and another at index n-1. Compute the combined value (sum, product, area). If it is too large, the right pointer must move inward, because pairing the current right with anything further right would only be larger. If too small, the left pointer must move inward by the same logic. Each step eliminates an entire row or column of the implicit n-by-n pair table. Total work is O(n) instead of O(n^2).
Same-direction (read/write) pointers. Used for in-place array transforms like removing duplicates, moving zeroes, or partitioning. A read pointer scans every element. A write pointer marks the next slot in the output region. The write pointer advances only when the read pointer finds an element worth keeping. Both pointers move forward and never backward, giving O(n) time and O(1) extra space.
The reason the technique is correct comes from the data invariant. For converging pointers, the sortedness of the array guarantees that one direction strictly increases and the other strictly decreases. For read/write pointers, the invariant is that everything before the write pointer is the cleaned-up output prefix, and everything between write and read has already been seen and discarded.
Sorted array pair problems, partitioning, removing duplicates in-place, or any problem where examining elements from both ends narrows the search space.
- Unsorted data that can't be sorted
- Need all pairs not just one (use hash map)
- Non-linear relationships between elements
- empty array
- all same values
- two elements
Key Points
- •Initialize pointers at opposite ends or same start
- •Move based on comparison with target
- •Handle duplicates by skipping equal elements
- •Works on sorted arrays for pair-finding problems
- •Can also use same-direction pointers for partitioning
Code Template
def two_pointers(arr, target):
"""Find pair in sorted array that sums to target."""
left, right = 0, len(arr) - 1
while left < right:
current = arr[left] + arr[right]
if current == target:
return [left, right]
elif current < target:
left += 1
else:
right -= 1
return [-1, -1]
# Same-direction variant: remove duplicates in-place
def remove_duplicates(arr):
if not arr:
return 0
write = 1
for read in range(1, len(arr)):
if arr[read] != arr[read - 1]:
arr[write] = arr[read]
write += 1
return writeCommon Mistakes
- Infinite loop when both pointers aren't advancing
- Not handling duplicate elements properly
- Forgetting to sort the array when required