patternDSA/Fast & Slow Pointers
Core Pattern → Pattern 09

Fast & Slow Pointers

Two pointers at different speeds. Fast catches slow if a cycle exists. Slow lands on the middle when fast hits the end. O(1) space, O(n) time — no HashSet needed.

Frequency:★★★★☆
·~35 min·Intermediate·7 problems
The Core Shock

Detect a cycle using a HashSet — O(n) space. Using fast/slow — O(1) space

HashSet Approach
O(n) space
Store every visited node — n nodes = n memory
O(n) space
Fast & Slow Pointers
O(1) space
Two pointers, no storage — regardless of n
O(1) space

The insight: two runners on a circular track always meet. Fast (2 steps) gains exactly 1 step on slow (1 step) per iteration. The gap closes by 1 each time — it must reach 0. They meet inside the cycle, proving it exists. No storage needed.

Two Uses of Fast & Slow

Every fast/slow problem is one of these two. Identify before coding.

Cycle Detection

Fast pointer catches slow inside a cycle. If fast hits null, no cycle. If they meet, cycle confirmed. Two-phase variant finds exact cycle start.

Test: Ask: 'Could this structure loop forever?' If yes → cycle detection.
LinkedList CycleStart of CycleHappy NumberCycle in Circular Array
Middle / Structural

When fast reaches end (null or last node), slow is exactly at the middle. Use to split a list, find middle for palindrome check, or restructure.

Test: Ask: 'Do I need the middle without counting?' If yes → middle finder.
Middle of LinkedListPalindrome LinkedListRearrange LinkedListReorder List

The 3-Step Framework

The same template works for every fast/slow problem.

01
Initialize Both at Head

Both slow and fast start at head (not head.next). The loop condition checks 'fast and fast.next' — both must be non-null before attempting fast.next.next.

slow, fast = head, head
while fast and fast.next:
'while fast and fast.next' — both checks are required. fast alone could be non-null while fast.next is null, making fast.next.next crash.
02
Advance at Different Speeds

slow = slow.next (1 step). fast = fast.next.next (2 steps). For cycle detection: check 'slow is fast' after advancing. For middle: just advance, no check needed.

slow = slow.next
fast = fast.next.next
if slow is fast:  # cycle!
Use 'is' not '==' — you're comparing object identity (same node in memory), not node values. Two different nodes can have the same value.
03
Two-Phase for Cycle Start (if needed)

After meeting point is found: reset slow to head, keep fast at meeting point. Advance both 1 step at a time. Where they meet again is the cycle start — mathematical proof guarantees it.

slow = head  # reset
while slow is not fast:
    slow = slow.next
    fast = fast.next
return slow  # cycle start
Phase 2 only needed for 'find cycle start' problems, not for 'detect cycle' or 'find middle'. Don't add phase 2 unless the problem asks for it.

Recognition

Fast/Slow vs other pointer patterns — choose correctly in 20 seconds.

Use Fast & Slow When
"Does this linked list / sequence have a cycle?"
"Find middle of linked list"
Palindrome check on linked list
Rearrange / reorder linked list
"Find start of cycle"
Happy Number / number sequence cycles
Problem says O(1) space required
Any "tortoise and hare" hint
Use Other Patterns When
"Find pair summing to target" → Two Pointers
"Shortest path in graph" → BFS
"Detect cycle in directed graph" → DFS color
"Remove nth from end" → Two Pointers (gap)
Array problems (not linked list) → Sliding Window
Sorted array search → Binary Search
"Count nodes" → just traverse once
Cycle in undirected graph → Union Find / DFS