Two Pointers is a technique that uses two pointers (indices) moving through a data structure (usually an array or string) in a single pass to solve problems that would otherwise require nested loops.
- Searching for a pair (or triplet) that satisfies a condition in a sorted array (e.g., two sum, three sum).
- Removing duplicates in‑place.
- Reversing an array or string.
- Checking for palindromes.
- Merging two sorted arrays.
- Sliding window problems (often combined with two pointers).
- Partitioning arrays (e.g., move zeros to end, segregate odd/even).
There are several common patterns:
- Place one pointer at the beginning (left), another at the end (right).
- While left < right:
- Perform some check or operation using arr[left] and arr[right].
- Move left forward or right backward (or both) based on the condition.
- This is great for sorted arrays or palindrome checking.
- Place both pointers at the start.
- One pointer moves faster (e.g., 2 steps at a time) while the other moves slower.
- Used for cycle detection (Floyd’s cycle detection) or finding middle of linked list.
- One pointer (i) iterates normally.
- The other pointer (j) only moves when a condition is met.
- Used for removing duplicates in‑place or moving elements.
Analogy: Looking for two people in a line whose heights sum to a target. One person starts at the left, one at the right. If their heights sum is too small, the left person steps right. If too large, the right person steps left. They meet in the middle.
Use when: checking if any two elements in a sorted array sum to a target.
Input: nums — sorted list of integers; target — desired sum.
Output: True if a valid pair exists, False otherwise.
def has_pair_sum(nums, target):
left, right = 0, len(nums) - 1
while left < right:
current_sum = nums[left] + nums[right]
if current_sum == target:
return True
elif current_sum < target:
left += 1
else:
right -= 1
return FalseUse when: reversing an array or string without extra space.
Input: arr — list of elements.
Output: arr reversed in place (no return value).
def reverse_array(arr):
left, right = 0, len(arr) - 1
while left < right:
arr[left], arr[right] = arr[right], arr[left]
left += 1
right -= 1Use when: finding the middle node of a linked list in one pass.
Input: head — head node of a linked list.
Output: the middle node (or second middle if even length).
def find_middle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slowUse when: compacting a sorted array to keep only unique values, in place.
Input: nums — sorted list of integers (modified in place).
Output: new length after removing duplicates.
def remove_duplicates(nums):
if not nums:
return 0
j = 0 # points to last unique element
for i in range(1, len(nums)):
if nums[i] != nums[j]:
j += 1
nums[j] = nums[i]
return j + 1 # new length- Time:
O(n)– each pointer moves at mostnsteps total. - Space:
O(1)– no extra data structures (modifies input or uses constant variables).
- Why does the opposing pointers technique require the array to be sorted for pair sum?
- What happens if you use opposing pointers on an unsorted array?
- When would you choose fast & slow pointers over opposing pointers?
Answers
-
The decision to move left or right depends on the current sum being too small or too large. That only works if the array is sorted – otherwise you can’t guarantee that moving left increases the sum.
-
The algorithm would give incorrect results because the ordering property is lost. For unsorted arrays, you’d need a hash set or sort first.
-
Fast & slow pointers are used for linked list problems (cycle detection, middle element) or problems where you need two different speeds. Opposing pointers are for arrays where you can index directly.
- Two Sum II – Input Array Is Sorted – classic opposing pointers.
- Valid Palindrome – opposing pointers on string.
- Remove Duplicates from Sorted Array – scanning left pointers.
- Container With Most Water – opposing pointers.
- Linked List Cycle – fast & slow pointers.
- Move Zeroes – scanning left pointers.
I used to think two pointers was just one pattern. But there are multiple: opposing, fast/slow, and same‑direction. Each is for different problem structures. For example, I once tried to use fast/slow on a sorted array pair sum – that didn’t work. Learning which pattern fits which problem type was the real aha moment.
- Sliding Window – often uses two pointers with a window.
- Binary Search – also uses left/right pointers but different logic.