A sorted array of a million elements holds half a trillion possible pairs. Ask which pair adds up to a target and brute force says to check them all. The Two Pointers technique finds the same pair in at most a million steps, one pass over the array, and it never gambles: every pair it skips is a pair it has proven can't work.
The trick starts with two pointers working through the same array or linked list from different positions. The most common setup is a left pointer on the first index and a right pointer on the last. As we process the data, we move these pointers inward or outward based on conditions specific to the problem, and every move quietly discards all the pairs that used the element left behind. That mass discard is where the speed comes from.
Two pointers come in flavors, and the difference comes down to two decisions: where each pointer starts and which way it moves. Opposite-direction pointers use a left pointer at the beginning and a right pointer at the end of an array. The pointers move toward each other based on comparing their values. If we're looking for a target sum, we move right when the sum is too large and left when it's too small.
Another pattern is sliding window, where both pointers move in the same direction while maintaining a specific window size. It's useful for finding subarrays with certain properties, but this page focuses on opposite-direction pointers.
Every pointer move is a claim that one element can be thrown away unchecked. Watch that claim play out on real values: in the sorted array [1, 3, 4, 6, 8, 11], find two values that sum to 10. Start with left on the 1 and right on the 11: they total 12, too big. Sliding left forward could only make the sum bigger, so no pair containing the 11 can ever reach 10. The 11 is out, and right steps inward. Now 1 + 8 = 9 is too small, and the mirrored argument retires the 1. Then 3 + 8 = 11 rules out the 8, 3 + 6 = 9 rules out the 3, and 4 + 6 = 10 hits the target. Five comparisons, and four of them each eliminated an element for good.
You might think this skipping is a clever heuristic, a way of guessing that could in principle miss a pair hiding among the combinations we never checked. What actually happens is stricter: a pointer only moves when the sorted order guarantees no remaining pair could use the element being abandoned. That guarantee is also the price of admission. On unsorted data the argument falls apart, which is why opposite-direction pointers expect sorted input.
The pointers keep closing in like the jaws of a clamp until they meet or the answer turns up. One caveat about the clamp picture: a real clamp tightens both jaws at once, while two pointers only ever advances the side it has proven useless. The technique is space-efficient because it only needs two variables, and it's time-efficient since each element is visited at most once. That works out to O(n) time and O(1) space for a job whose brute force costs O(n²).
0) and one at the end (index length - 1) of an array. With the pointers at the extremes, every possible pair sits between them, so nothing has been ruled out before the search starts.Opposite-direction pointers typically run inside a while loop until the pointers meet. The pattern works best when you need to compare elements from opposite ends of sorted data. In sorted arrays, moving pointers has predictable effects: moving right always gives a smaller value, while moving left gives a larger one. That predictability is what makes it easy to home in on a target. The template below is the entire pattern in code. Before scrolling past it, run it in your head on [1, 3, 4, 6, 8, 11] with a target of 10 and commit to two predictions: how many sums does the loop compute, and exactly what does the function return?
class Solution:
def twoPointers(self, numbers: List[int], target: int) -> List[int]:
# Step 1: Initialize two pointers
left, right = 0, len(numbers) - 1
# Step 2: Move the pointers
while left < right:
# Step 3: Compare and process
total = numbers[left] + numbers[right]
if total < target:
left += 1
elif total > target:
right -= 1
# Step 4: Terminate (target found)
else:
return [left + 1, right + 1]
# Step 4: Terminate (when loop ends)
return []The loop computes five sums, the same trace from earlier on this page. The return value is the sneaky part: the function hands back [3, 4], not the zero-based indices [2, 3], because the template returns left + 1 and right + 1 to match the one-indexed convention some problems ask for. Check the return line before assuming an off-by-one bug.
Palindrome problems are another natural fit for Two Pointers because they require comparing characters from both ends of a string. By checking whether characters match as we move inward, we can validate palindromes without storing or comparing the entire string up front.
Two Pointers is one of the most reliable patterns for solving array, string, and linked list problems efficiently. Here are a few tips for implementing it well and avoiding the common pitfalls:
Before you start, quiz yourself on two things from this page: when a sum comes up too large, why is it always safe to retire the right element without checking its other pairs, and what property of the input does that proof depend on? If both answers come back quickly, you're ready. The two-pointers technique converts a lot of "try every pair" brute force into clean linear scans. Work through these to internalize convergent (left/right) pointers, fast/slow write pointers, and the trickier variants where the two-pointer logic stitches into sliding window or DP:
Completed | Title | Status | Notes | Solution | |
|---|---|---|---|---|---|
Easy | |||||
Easy | |||||
Easy | |||||
Easy | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Hard | |||||
Hard | |||||
Hard | |||||
Hard |