What Is the Two Pointers Pattern?
The Two Pointers pattern uses two index variables — often called left and right, or slow and fast — that traverse a data structure. Instead of using nested loops to compare every pair in O(n²), two pointers exploit the structure of the problem (usually sorted data or a specific pointer speed relationship) to achieve O(n) time with O(1) extra space.
It is one of the foundational patterns in competitive programming and technical interviews, and appears across arrays, strings, and linked list problems at every difficulty level.
Two pointers work because they eliminate impossible candidates. On a sorted array, if the sum of arr[left] + arr[right] is too large, you know every element to the right of left is also too large — so you move right inward. This is the key insight behind all converging pointer problems.
When to Use Two Pointers
Look for these signals in the problem statement:
- The array or string is sorted (converging pointers)
- You need to find a pair, triplet, or subarray satisfying a condition
- The problem involves a linked list and cycle detection or midpoint finding
- You need to modify an array in-place without extra space (remove duplicates, move zeroes)
- You need to reverse a string, array, or parts of them in-place
- You need to compare characters from both ends of a string (palindrome check)
- A brute-force O(n²) solution exists and you want to optimize to O(n)
The 7 Two Pointers Sub-patterns
Pattern 1: Converging Pointers (Sorted Array Target Sum)
The most classic Two Pointers form. Start left at index 0 and right at the last index. Move them based on whether the current combination satisfies, exceeds, or falls short of the target.
def twoSum(numbers, target):
left, right = 0, len(numbers) - 1
while left < right:
s = numbers[left] + numbers[right]
if s == target:
return [left + 1, right + 1] # 1-indexed
elif s < target:
left += 1 # sum too small, move left forward
else:
right -= 1 # sum too large, move right backward
return []
3Sum extension: Fix one element with a loop, then run Two Pointers on the remaining subarray. Handle duplicates by skipping equal elements.
def threeSum(nums):
nums.sort()
result = []
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i-1]: continue # skip duplicate
left, right = i + 1, len(nums) - 1
while left < right:
s = nums[i] + nums[left] + nums[right]
if s == 0:
result.append([nums[i], nums[left], nums[right]])
while left < right and nums[left] == nums[left+1]: left += 1
while left < right and nums[right] == nums[right-1]: right -= 1
left += 1; right -= 1
elif s < 0: left += 1
else: right -= 1
return result
Pattern 2: Fast & Slow Pointers (Floyd's Algorithm)
The slow pointer moves one step; the fast pointer moves two. If there's a cycle, they'll meet inside it. If there's no cycle, fast reaches the end. This also finds the middle of a linked list in one pass — when fast reaches the end, slow is at the midpoint.
def hasCycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True # cycle detected — they met
return False # fast reached end — no cycle
def middleNode(head):
# When fast reaches end, slow is at middle
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
Pattern 4: In-place Array Modification (Read/Write Pointers)
Use a write pointer (slow) that only advances when a valid element is found, and a read pointer (fast) that scans everything. This filters arrays in O(n) with O(1) space.
def removeDuplicates(nums):
if not nums: return 0
write = 1 # position to write next unique element
for read in range(1, len(nums)):
if nums[read] != nums[read - 1]: # found new unique
nums[write] = nums[read]
write += 1
return write
Pattern 6: Expand From Center (Palindromes)
For each index (and each pair of adjacent indices), expand outward as long as the characters match. Track the longest expansion found. This solves all palindrome-finding problems in O(n) time.
def longestPalindrome(s):
def expand(l, r):
while l >= 0 and r < len(s) and s[l] == s[r]:
l -= 1; r += 1
return s[l+1:r] # l,r went one step too far
result = ""
for i in range(len(s)):
odd = expand(i, i) # odd-length palindrome
even = expand(i, i + 1) # even-length palindrome
if len(odd) > len(result): result = odd
if len(even) > len(result): result = even
return result
All Two Pointers LeetCode Problems
28 curated problems across 5 sub-patterns. Start with Converging (easiest), then Fast & Slow, In-place, Palindrome, Reversal.
Interview Tips & Common Mistakes
Converging pointers only work on sorted data. If the problem doesn't guarantee sorted order, sort first (O(n log n)) — the Two Pointers pass is still O(n), so the total is O(n log n), better than O(n²) brute force.
In 3Sum, 4Sum and similar, you must skip duplicate values after finding a valid triplet to avoid duplicate result sets. After a match, advance both pointers then skip equal consecutive values.
The fast & slow trick doubles as a midpoint finder: when fast reaches the end, slow is at the middle. This is used in Palindrome Linked List (#234) — find mid, reverse second half, compare.
In converging pointers, only move one pointer per iteration based on the comparison result. Never move both simultaneously unless you've found a match — this skips valid pairs.
Always check fast and fast.next before advancing. Checking only fast.next will crash if fast is already None when entering the loop condition.
🗺️ Explore More DSA Patterns
Two Pointers FAQ
What is the Two Pointers pattern in LeetCode?
The Two Pointers pattern uses two index variables that traverse a data structure — often from opposite ends or at different speeds. It reduces O(n²) brute force to O(n) by leveraging the sorted or structured nature of the data to eliminate impossible candidates without checking every pair.
When should I use Two Pointers vs Sliding Window?
Use Two Pointers when comparing or combining elements at two positions that may not form a contiguous range (like two ends of a sorted array), or when moving at different speeds (fast & slow). Use Sliding Window when the problem cares about the contents of the subarray between the two pointers (frequency, sum, length). Sliding Window is a specialized form of Two Pointers.
What is the fast and slow pointer (Floyd's algorithm)?
Floyd's cycle detection moves one pointer one step and another pointer two steps. If there is a cycle, both pointers enter it and the fast one catches up to the slow one from behind — they meet inside the cycle. This runs in O(n) time and O(1) space, making it ideal for cycle detection in linked lists and implicit graphs.
What are the most important Two Pointers problems to practice?
The six must-solve problems are: Two Sum II (converging), 3Sum (fix one + converge), Container With Most Water (greedy converge), Linked List Cycle II (fast & slow cycle entry), Longest Palindromic Substring (expand center), and Sort Colors (Dutch National Flag with 3 pointers). These cover every major variant.
Can Two Pointers work on unsorted arrays?
Converging pointers require sorted data. However, fast & slow pointers, in-place modification (read/write), expand-from-center, and reversal patterns all work on unsorted or arbitrary arrays. If the problem asks for pairs from an unsorted array, sort first — the O(n log n) cost is still better than O(n²) brute force.
- Sliding Window Pattern Guide – LeetCode Problems & Templates — the natural next step after two pointers
- Practice Two Pointers on the Tracker — track your progress across all 33 problems
- Full DSA Patterns Tracker — all 88 patterns in one place
- 8-Week Interview Study Plan — where Two Pointers fits