Two pointers uses two index variables to traverse a data structure, typically an array or string. The pointers move towards each other, away from each other, or in the same direction based on the problem's requirements.
Using this approach, you can reduce the time complexity of many array and string problems from O(n²) to O(n), or to O(n log n) when sorting is required first.
This chapter covers what the Two Pointers pattern is, the common variants, and when to use it.
A pointer is a variable that represents an index or position within a data structure, such as an array or linked list.
The pointers can represent:
Using pointers at different positions, we can compare elements and make decisions without relying on nested loops, which would otherwise lead to O(n²) time complexity.
The Two Pointers technique can be applied in different ways depending on the problem. Here are the three most common strategies.
In the most common variant, one pointer starts at the beginning, the other at the end, and they move towards each other.
The pointers adjust their positions based on comparisons, until a certain condition is met, or they cross each other.
This strategy is ideal for problems where we need to compare elements from opposite ends of an array or string.
Which pointer to move depends on the comparison. In sorted arrays:
left right increases values (if sorted ascending)right left decreases values (if sorted ascending)Opposite-direction two pointers for pair-sum problems such as Two Sum II and 3Sum requires the array to be sorted. The technique relies on monotonicity: increasing left only increases the sum, and decreasing right only decreases it. Without that property, you cannot decide which pointer to move after a comparison.
Opposite-direction two pointers for palindrome or mirror checks does not require sorting. It relies on position symmetry, not value order, so the comparison is between characters at mirrored indices.
Same-direction variants such as read/write partition, sliding window, and fast/slow pointers generally do not require sorting either.
When sorting is required and the input is unsorted, the overall complexity becomes O(n log n) for the sort plus O(n) for the two-pointer scan, dominated by the sort.
Time: O(n). Space: O(1).
Checking if a string is a palindrome
A palindrome is a sequence that reads the same forward and backward (e.g., "racecar", "madam").
To check if a string is a palindrome, we:
false.In this approach, both pointers start at the same end (usually the beginning) and move in the same direction at different speeds or for different purposes.
These pointers serve two different but complementary roles:
left pointer is the write pointer or boundary, tracking progress and marking where valid elements end.right pointer is the read or explore pointer, scanning ahead looking for the next element to process.Here are the two popular variations of this approach:
The standard speed choice is 1 step for the slow pointer and 2 steps for the fast pointer. If the linked list has a cycle, the slow pointer eventually enters the cycle and starts looping around it. The fast pointer is also looping around the cycle but moves twice as fast.
The relative speed of the fast pointer with respect to the slow pointer is 1 step per iteration: fast advances 2, slow advances 1, and the net difference is 1. Once both pointers are inside the cycle, the fast pointer gains exactly 1 step on slow every iteration. Since the cycle has finite length L, fast catches up to slow within at most L iterations.
If the list has no cycle, the fast pointer reaches the end of the list (null) first. That is the termination condition for the no-cycle case.
To find the starting node of the cycle after detection, reset one pointer to the head and advance both pointers at speed 1. They meet at the cycle's entry node. This is a standard interview follow-up to plain cycle detection.
Time: O(n). Space: O(1).
The general same-direction template (read/write partition) also runs in Time: O(n) and Space: O(1), since each pointer advances at most n times across the array.
This is a specific same-direction pattern, not a separate variant. Both pointers move in the same direction, but one is advanced first to create a fixed gap between them before they move together.
In this approach, we move the first pointer independently until it finds an element that meets a certain condition. After this, we start traversing with the second pointer to find additional information related to what the first pointer found.
The pattern applies when we need to process elements in stages or maintain a fixed distance between two pointers.
A good example of this approach is finding the Nth node from the end of a linked list.
fast and slow) at the head.fast pointer n steps.fast pointer reaches the end.slow pointer is now at the Nth node from the end.Time: O(n). Space: O(1).
A Two-Pointer algorithm is generally applied to linear data structures, such as: array, strings or linked lists.
Two pointers fits problems where the input data follows a predictable pattern, such as a sorted array or palindromic string. Common patterns include:
1. Sorted array with pair/triplet finding
If the array is sorted (or can be sorted) and you need to find elements that satisfy some relationship, two pointers can often replace nested loops.
2. In-place array modification
When you need to modify an array without using extra space, the read-write pointer pattern works well.
3. Palindrome or symmetry checking
Comparing elements from both ends leads to two pointers converging.
4. Merging or comparing sorted sequences
When working with two sorted arrays, using a pointer for each is the natural approach.
5. Subarray problems with monotonic conditions
If expanding a window changes a condition in a predictable direction, two pointers can work. This overlaps with sliding window, which is two pointers with specific semantics.
10 quizzes