Two Pointers Pattern: How to Solve Interview Problems with Less Noise and More Signal
Learn the two pointers pattern for sorted arrays, pair sum, and LeetCode-style interview problems with clear Python examples and walkthroughs.
Why two pointers matter in interviews
The two pointers pattern shows up everywhere in coding interviews because it lets you turn an expensive search into a linear pass. Instead of checking every pair or every subarray with nested loops, you keep two indices that move in a disciplined way. That simple idea solves a lot of problems on sorted arrays, linked lists, and strings.
If you learn when to use two pointers, you can spot a solution faster and write cleaner code under pressure. More importantly, you can explain why it works, which is usually what interviewers care about.
The pattern is especially common in LeetCode-style problems involving:
- a sorted array
- pair sum / triplet sum
- removing duplicates
- partitioning by a condition
- checking palindromes or mirrored structure
Problem statement: what is the two pointers pattern?
The core idea is simple:
- Keep two indices, often named
leftandright - Start them at opposite ends or nearby positions
- Move one or both pointers based on the current values
- Use the movement rule to avoid redundant work
There are a few common variants:
- Opposite ends: best for sorted arrays or palindrome checks
- Same direction: best for filtering, merging, or sliding over ordered data
- Fast and slow pointers: a related technique for cycle detection and linked list problems
This post focuses on the classic two pointers pattern used on arrays and strings, especially when a sorted array gives you useful order.
Intuition: why two pointers work
Two pointers work because the input order gives you information.
Imagine a sorted array and you want a pair sum. If the sum is too small, moving the left pointer right increases the sum. If the sum is too large, moving the right pointer left decreases the sum.
That monotonic behavior is the key. You are not guessing randomly; each pointer move eliminates a whole set of impossible answers.
Example mental model
For a sorted array:
- small value on the left
- large value on the right
- compare the sum to the target
- adjust the side that can move the sum in the needed direction
This gives you an O(n) scan instead of O(n^2) nested loops.
Approach walkthrough: pair sum in a sorted array
Let’s start with the classic interview problem:
Given a sorted array of integers and a target, return whether there exists a pair whose sum equals the target.
Why two pointers fit
Because the array is sorted, the smallest possible sum is at the left side and the largest possible sum is at the right side. You can steer the sum toward the target without checking every pair.
Algorithm
- Set
left = 0andright = len(nums) - 1 - While
left < right:- Compute
total = nums[left] + nums[right] - If
total == target, returnTrue - If
total < target, moveleft += 1 - If
total > target, moveright -= 1
- Compute
- If the loop ends, return
False
Python code
def has_pair_with_sum(nums, target):
left, right = 0, len(nums) - 1
while left < right:
total = nums[left] + nums[right]
if total == target:
return True
elif total < target:
left += 1
else:
right -= 1
return FalseWorked example 1
Suppose:
nums = [1, 2, 4, 6, 8, 9]
target = 11Step by step:
| left | right | nums[left] | nums[right] | total | action |
|---|---|---|---|---|---|
| 0 | 5 | 1 | 9 | 10 | total < target, move left |
| 1 | 5 | 2 | 9 | 11 | found it |
The function returns True quickly, without checking all pairs.
Worked example 2
Suppose:
nums = [1, 3, 4, 5, 7, 10]
target = 15| left | right | sum | action |
|---|---|---|---|
| 0 | 5 | 11 | move left |
| 1 | 5 | 13 | move left |
| 2 | 5 | 14 | move left |
| 3 | 5 | 15 | found it |
Again, the sorted order tells you exactly how to move.
A more useful version: return the pair itself
Interviewers often ask for the actual values or indices. Here is the same idea returning the pair of values:
def find_pair_with_sum(nums, target):
left, right = 0, len(nums) - 1
while left < right:
total = nums[left] + nums[right]
if total == target:
return [nums[left], nums[right]]
elif total < target:
left += 1
else:
right -= 1
return []Example
nums = [2, 5, 9, 12, 15]
target = 14
print(find_pair_with_sum(nums, target))Output:
[2, 12]Common two pointers use cases
Two pointers is broader than pair sum. Here are the patterns you should recognize.
1. Opposite ends for sorted arrays
Use this when:
- the array is sorted
- you need to compare extremes
- the rule for movement is clear
Examples:
- pair sum
- container with most water
- palindrome validation
- removing elements from ends
2. Same direction for filtering
Use this when one pointer scans and the other writes.
Examples:
- remove duplicates from a sorted array
- remove a target value in-place
- partitioning arrays
3. Fast and slow pointers
This is related, but slightly different. It’s useful for:
- linked list cycle detection
- finding the middle of a linked list
- in-place list transformations
Step-by-step approach to solving a two pointers problem
When you see a new problem, ask these questions:
- Is the input sorted, or can I sort it?
- Can I move one pointer to make progress based on the current state?
- Does moving left or right always improve or worsen the answer in a predictable way?
- Can I maintain an invariant?
If the answer to these is yes, two pointers is probably a good fit.
General template
def two_pointers_template(nums):
left, right = 0, len(nums) - 1
while left < right:
# inspect state
# decide whether to move left, right, or both
# update answer if needed
passThe important part is not the template itself. It is the movement rule.
Worked example 3: remove duplicates from a sorted array
This is a classic same-direction two pointers problem.
Given a sorted array, remove duplicates in-place and return the new length.
Idea
Use:
slowas the position to write the next unique valuefastas the scanner
If nums[fast] is different from the last unique value, copy it forward.
Python code
def remove_duplicates(nums):
if not nums:
return 0
slow = 1
for fast in range(1, len(nums)):
if nums[fast] != nums[fast - 1]:
nums[slow] = nums[fast]
slow += 1
return slowExample
nums = [1, 1, 2, 2, 2, 3, 4, 4]
length = remove_duplicates(nums)
print(length)
print(nums[:length])Output:
4
[1, 2, 3, 4]Why this works
Because the array is sorted, duplicates are adjacent. You only need to compare each element with the previous one. The slow pointer marks the boundary between processed unique values and the rest.
Worked example 4: palindrome check with two pointers
Two pointers also works well for strings.
Check whether a string is a palindrome.
Idea
Compare matching characters from both ends and move inward.
Python code
def is_palindrome(s):
left, right = 0, len(s) - 1
while left < right:
if s[left] != s[right]:
return False
left += 1
right -= 1
return TrueExample
print(is_palindrome("racecar"))
print(is_palindrome("hello"))Output:
True
FalseThis is the same pattern: two ends, one comparison rule, and a clear movement strategy.
Complexity analysis
Two pointers usually improves a problem from quadratic to linear time.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute force pair search | O(n^2) | O(1) | Check every pair |
| Two pointers on sorted array | O(n) | O(1) | Best when order helps |
| Sort + two pointers | O(n log n) | O(1) or O(log n) | Sorting may dominate |
| Same-direction scan | O(n) | O(1) | Good for in-place filtering |
Important detail
If the array is not already sorted and you need pair sum by value, sorting may be required first. That changes the total runtime to O(n log n). Still, the two pointers scan itself stays O(n).
How to recognize the pattern quickly
Use two pointers when you see:
- a sorted array
- a goal involving a pair sum or range
- a need to compare left vs right
- in-place cleanup or partitioning
- a monotonic movement rule
A good interview habit is to say out loud:
- what each pointer means
- why moving one pointer helps
- why you do not need to revisit old states
That explanation often matters as much as the code.
Common mistakes
1. Moving both pointers without a reason
Only move both when the current state proves both sides are no longer needed.
2. Using two pointers on unsorted data without thinking
If the data is unsorted, the movement rule may not be valid.
3. Forgetting the loop boundary
For opposite-end pointers, use while left < right, not <=, unless you specifically want to inspect the middle element.
4. Not defining the invariant
You should know what remains true after each move. For example:
- all values left of
sloware unique - all pairs outside the current window are impossible
- the pointers always move toward convergence
Related problems
Two pointers connects to many interview problems:
- Two Sum II on a sorted array
- 3Sum and 4Sum
- Container With Most Water
- Valid Palindrome
- Remove Duplicates from Sorted Array
- Merge Sorted Array
- Squares of a Sorted Array
These problems often differ in the goal, but the pointer movement logic is the same.
Key takeaways
- Two pointers reduce repeated work by using ordered movement.
- The pattern works best when the input is sorted or behaves monotonically.
- Opposite-end pointers are common for pair sum and palindrome-style problems.
- Same-direction pointers are common for filtering and in-place updates.
- The hard part is not writing the loop; it is choosing the correct movement rule.
Practice these next
If you want to build fluency, try these problems next:
- Two Sum II - Input Array Is Sorted
- 3Sum
- Container With Most Water
- Valid Palindrome
- Remove Duplicates from Sorted Array
If you'd like to see the variables update live as you step through, check out codebrew.ai.

