šš Two Pointers Pattern
The gateway pattern to coding interviews. Master this first.
TL;DRā
Two pointers uses two indices to traverse data structures, reducing O(n²) brute force to O(n).
Sorted array + find pair = Two Pointers
The Three Variantsā
1. Opposite Ends (Most Common)ā
Left āāāāāāāāāāāāāāāāāŗ Middle āāāāāāāāāāāāāāāāā Right
L R
When to use: Sorted array, find pair, palindrome, containers
2. Same Directionā
Slow āāāāāāŗ Fast āāāāāāāāāāāāāāāāāāŗ
S F
When to use: Remove duplicates, partition, merge sorted
3. Fast/Slow (Floyd's)ā
āāāāāāāāāāāāāāāā
S F
(slow) (fast: 2x speed)
When to use: Cycle detection, find middle
Pattern 1: Opposite Endsā
Templateā
def two_pointer_opposite(arr, target):
left, right = 0, len(arr) - 1
while left < right:
current = arr[left] + arr[right]
if current == target:
return [left, right]
elif current < target:
left += 1 # Need bigger sum
else:
right -= 1 # Need smaller sum
return []
Problem: Two Sum II (Sorted Array)ā
Given sorted array, find two numbers that add to target.
def two_sum(numbers: list[int], target: int) -> list[int]:
left, right = 0, len(numbers) - 1
while left < right:
total = numbers[left] + numbers[right]
if total == target:
return [left + 1, right + 1] # 1-indexed
elif total < target:
left += 1
else:
right -= 1
return []
# Example
# numbers = [2, 7, 11, 15], target = 9
# left=0 (2), right=3 (15): 17 > 9, right--
# left=0 (2), right=2 (11): 13 > 9, right--
# left=0 (2), right=1 (7): 9 == 9, return [1, 2]
Why it works:
- Sum too small ā need bigger number ā move left right
- Sum too big ā need smaller number ā move right left
- Sorted array guarantees we don't miss any pair
Interactive Demoā
Two Sum II (Sorted Array)
View Code
def two_sum(numbers, target):
left, right = 0, len(numbers) - 1
while left < right:
total = numbers[left] + numbers[right]
if total == target:
return [left, right]
elif total < target:
left += 1
else:
right -= 1
return []Problem: Valid Palindromeā
Check if string is palindrome (letters/digits only).
def is_palindrome(s: str) -> bool:
left, right = 0, len(s) - 1
while left < right:
# Skip non-alphanumeric
while left < right and not s[left].isalnum():
left += 1
while left < right and not s[right].isalnum():
right -= 1
if s[left].lower() != s[right].lower():
return False
left += 1
right -= 1
return True
# "A man, a plan, a canal: Panama" ā True
# "race a car" ā False
Interactive Demoā
Valid Palindrome
View Code
def is_palindrome(s):
left, right = 0, len(s) - 1
while left < right:
if s[left].lower() != s[right].lower():
return False
left += 1
right -= 1
return TrueProblem: Container With Most Waterā
Find max water between two lines.
| | |
| | | | |
| | | | | | | |
| | | | | | | |
0 1 2 3 4 5 6 7
def max_area(height: list[int]) -> int:
left, right = 0, len(height) - 1
max_water = 0
while left < right:
width = right - left
h = min(height[left], height[right])
max_water = max(max_water, width * h)
# Move the shorter line inward
if height[left] < height[right]:
left += 1
else:
right -= 1
return max_water
Key insight: Always move the shorter line. Moving the taller one can only decrease (or maintain) area.
Interactive Demoā
Container With Most Water
View Code
def max_area(height):
left, right = 0, len(height) - 1
max_water = 0
while left < right:
width = right - left
h = min(height[left], height[right])
max_water = max(max_water, width * h)
if height[left] < height[right]:
left += 1
else:
right -= 1
return max_waterPattern 2: Same Directionā
Templateā
def two_pointer_same_direction(arr):
slow = 0
for fast in range(len(arr)):
if condition(arr[fast]):
arr[slow] = arr[fast]
slow += 1
return slow # New length
Problem: Remove Duplicates from Sorted Arrayā
In-place removal, return new length.
def remove_duplicates(nums: list[int]) -> int:
if not nums:
return 0
slow = 1 # Position for next unique element
for fast in range(1, len(nums)):
if nums[fast] != nums[fast - 1]:
nums[slow] = nums[fast]
slow += 1
return slow
# [1, 1, 2, 2, 3] ā [1, 2, 3, _, _], returns 3
Before: [1, 1, 2, 2, 3]
S F nums[F] == nums[F-1], skip
S F nums[F] != nums[F-1], copy & move S
S F skip
S F copy & move S
After: [1, 2, 3, 2, 3]
^^^^^^^ valid
Problem: Move Zeroesā
Move all 0s to end, maintain order.
def move_zeroes(nums: list[int]) -> None:
slow = 0
for fast in range(len(nums)):
if nums[fast] != 0:
nums[slow], nums[fast] = nums[fast], nums[slow]
slow += 1
# [0, 1, 0, 3, 12] ā [1, 3, 12, 0, 0]
Pattern 3: Fast/Slow (Floyd's)ā
Problem: Linked List Cycleā
Detect if cycle exists.
def has_cycle(head) -> bool:
slow = fast = head
while fast and fast.next:
slow = slow.next # Move 1 step
fast = fast.next.next # Move 2 steps
if slow == fast:
return True
return False
Why it works: If there's a cycle, fast eventually "laps" slow. If no cycle, fast hits null.
Problem: Find Middle of Linked Listā
def find_middle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow
# 1 ā 2 ā 3 ā 4 ā 5
# When fast reaches 5, slow is at 3
Advanced: 3Sumā
The classic FAANG problem.
Find all triplets that sum to zero.
def three_sum(nums: list[int]) -> list[list[int]]:
nums.sort() # Critical: enables two pointers
result = []
for i in range(len(nums) - 2):
# Skip duplicate first elements
if i > 0 and nums[i] == nums[i - 1]:
continue
left, right = i + 1, len(nums) - 1
while left < right:
total = nums[i] + nums[left] + nums[right]
if total == 0:
result.append([nums[i], nums[left], nums[right]])
# Skip duplicates
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 total < 0:
left += 1
else:
right -= 1
return result
# [-1, 0, 1, 2, -1, -4]
# Sorted: [-4, -1, -1, 0, 1, 2]
# Output: [[-1, -1, 2], [-1, 0, 1]]
Time: O(n²) - O(n log n) sort + O(n) for each of n elements Space: O(1) excluding output
Decision Flowchartā
Practice Problems (Grind 75)ā
| Problem | Difficulty | Pattern |
|---|---|---|
| Valid Palindrome | Easy | Opposite ends |
| Two Sum II | Medium | Opposite ends |
| 3Sum | Medium | Fix + opposite ends |
| Container With Most Water | Medium | Opposite ends |
| Remove Duplicates | Easy | Same direction |
| Move Zeroes | Easy | Same direction |
| Linked List Cycle | Easy | Fast/slow |
| Middle of Linked List | Easy | Fast/slow |
| Linked List Cycle II | Medium | Fast/slow + math |
| Trapping Rain Water | Hard | Opposite ends |
Quick Referenceā
| Variant | When to Use | Template |
|---|---|---|
| Opposite | Sorted + pair search | left=0, right=n-1 |
| Same | In-place modify | slow for placing, fast for scanning |
| Fast/Slow | Linked list | slow=1x, fast=2x |
Key insight: Two pointers reduce O(n²) ā O(n) by eliminating redundant comparisons.
Next: Sliding Window ā - For subarray/substring problems.