The fast and slow pointers pattern walks two pointers through the same sequence at different speeds (slow moves one step, fast moves two) so that they meet if there is a cycle, or so that slow stops at the middle when fast reaches the end. Reach for it when a problem gives you a linked list or a "next value" rule and asks about a cycle, where the cycle starts, or the middle element, especially when it wants O(1) extra space.
It is also called Floyd's cycle detection or the tortoise and hare algorithm. It is a special case of the two pointers pattern: both pointers move in the same direction, just at different speeds. This guide gives you the signals, a Python template, the short proof behind it, three worked LeetCode problems and a practice list. Every snippet below was run against test cases in Python before we published it.
When to use the fast and slow pointers pattern
Look for these signals in the problem statement:
- "Does it loop?" A linked list that may have a cycle, or a process that repeats until it hits 1 or loops forever (Happy Number).
- "Where does the loop start?" You need the entry node of a cycle, not just a yes/no answer.
- "Find the middle" of a linked list in one pass, often as step one of a bigger problem (palindrome check, reorder list, merge sort on a list).
- An array where each value points to an index. If values are in range
[1, n], theni → nums[i]is an implicit linked list, and a repeated value creates a cycle (Find the Duplicate Number). - A space limit. "Solve it with O(1) memory" or "do not modify the input" rules out a hash set of visited nodes and the cyclic sort trick.
If the problem is about pairs in a sorted array (two sum, 3Sum, container with most water), that is the opposite-ends version of two pointers instead. If it is about a contiguous range, see the sliding window pattern.
The Python template
Two functions cover most problems: one detects a cycle, the other finds the middle. Both use the same loop guard.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def has_cycle(head):
slow = fast = head
while fast and fast.next: # fast needs two valid steps
slow = slow.next # 1 step
fast = fast.next.next # 2 steps
if slow is fast: # compare nodes, not values
return True
return False
def middle_node(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
return slow # second middle when the length is even
The guard while fast and fast.next is the whole safety net. If either is None, the list ended, so there is no cycle. That one line handles empty lists, single nodes and both odd and even lengths.
Why the fast and slow pointers meet (the short proof)
Interviewers often ask "why does this work?" after you code it. Here is the argument in plain terms.
- They must meet. Once both pointers are inside the cycle, fast gains exactly one node on slow every step. The gap shrinks by one each time, so it hits zero within one lap. Fast can never jump over slow.
- Where the cycle starts. Let
abe the distance from the head to the cycle start,bthe distance from the cycle start to the meeting point, andLthe cycle length. Slow walkeda + b; fast walked twice that. The extra distance fast covered is whole laps, soa + b = kLfor some whole numberk. - Rearranged,
a = kL - b. Walkingasteps from the meeting point therefore lands exactly on the cycle start. So reset one pointer to the head, move both one step at a time, and they meet at the start.
That second phase is what turns "is there a cycle?" into "where does it begin?" without any extra memory.
Worked problems
1. Linked List Cycle II (LeetCode 142, Medium)
Problem: return the node where the cycle begins, or None if there is no cycle. The problem says not to modify the list and its follow-up asks for O(1) memory, which is exactly what Linked List Cycle II is testing.
Idea: phase 1 finds a meeting point inside the cycle. Phase 2 starts a new pointer at the head and moves it in lockstep with slow; by the proof above, they meet at the cycle start.
def detect_cycle(head):
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow is fast: # phase 1: they met inside the cycle
ptr = head
while ptr is not slow: # phase 2: same distance to the start
ptr = ptr.next
slow = slow.next
return ptr
return None
Time: O(n). Space: O(1). If the cycle starts at the head (a = 0), phase 2 returns immediately, which is correct.
2. Happy Number (LeetCode 202, Easy)
Problem: repeatedly replace a number with the sum of the squares of its digits. Return true if it reaches 1, false if it loops forever.
Idea: there is no linked list, but the "next number" rule is a next pointer. Every sequence either reaches 1 (which maps to itself) or falls into a cycle that does not contain 1. Run slow with one step and fast with two; if fast reaches 1 the number is happy, and if they meet first it is not.
def is_happy(n):
def step(x):
total = 0
while x:
x, digit = divmod(x, 10)
total += digit * digit
return total
slow, fast = n, step(n)
while fast != 1 and slow != fast:
slow = step(slow)
fast = step(step(fast))
return fast == 1
Time: O(log n) per step (the number of digits). For any input up to 10 digits, one step gives at most 10 × 81 = 810, so the sequence stays small and the loop is short. Space: O(1). A hash set of seen numbers also works and is fine to mention, but the two-pointer version is the answer to "can you do it without extra memory?"
3. Find the Duplicate Number (LeetCode 287, Medium)
Problem: nums has n + 1 integers, each in [1, n], and exactly one value repeats (possibly more than twice). You may not modify the array and may use only constant extra space.
Idea: treat index i as a node whose next pointer is nums[i]. Index 0 is never a target (values start at 1), so it is a safe head. Two indices point to the duplicate value, so the duplicate is the entrance of the cycle. Run the same two phases as Linked List Cycle II, on indices instead of nodes.
def find_duplicate(nums):
slow = fast = nums[0]
while True: # phase 1: meet inside the cycle
slow = nums[slow]
fast = nums[nums[fast]]
if slow == fast:
break
slow = nums[0] # phase 2: walk to the cycle entrance
while slow != fast:
slow = nums[slow]
fast = nums[fast]
return slow
Time: O(n). Space: O(1), and the array is untouched. This is why the cyclic sort pattern (which swaps values into place) does not fit this problem even though the values are in [1, n].
Complexity of fast and slow pointer solutions
| Task | Time | Extra space | Hash-set alternative |
|---|---|---|---|
| Detect a cycle | O(n) | O(1) | O(n) time, O(n) space |
| Find the cycle start | O(n) | O(1) | O(n) time, O(n) space |
| Find the middle node | O(n), one pass | O(1) | Count, then walk: two passes |
Duplicate in [1, n] without modifying | O(n) | O(1) | Set uses O(n); sorting modifies or copies |
| Happy Number | O(log n) per step | O(1) | Set of seen sums |
Need a refresher on why O(n) beats O(n log n) at scale? Try the free Big-O complexity visualizer or the Big-O cheat sheet.
Common bugs and edge cases
- Wrong loop guard.
while fast.next and fast.next.nextcrashes on an empty list.while fast and fast.nextis safe for every length. - Comparing values instead of nodes. Two different nodes can hold the same value. Use
slow is fastfor nodes. (Integers in Happy Number and Find the Duplicate are fine to compare with==because there the value is the node.) - Checking for a meeting before moving. Both pointers start at the head, so
if slow is fastbefore the first move returns true on every list. Move first, then compare. - Which middle? Starting both at
headgives the second middle on even lengths (what LeetCode 876 wants). For the first middle, which you want when splitting a list for merge sort, start withfast = head.next. - Phase 2 speed. In phase 2 both pointers move one step. Leaving fast at two steps is a common slip that loops forever.
- Single node with a self-loop.
head.next = headis a cycle starting at the head. Test it.
Practice list: 9 fast and slow pointer problems
Work through these in order. Difficulty labels are LeetCode's own, checked as of October 2026.
| # | Problem | Difficulty | What it adds |
|---|---|---|---|
| 141 | Linked List Cycle | Easy | The base template |
| 876 | Middle of the Linked List | Easy | Middle-finding, second middle on even lengths |
| 202 | Happy Number | Easy | Cycle in a number sequence, no list |
| 234 | Palindrome Linked List | Easy | Find the middle, reverse the second half, compare |
| 142 | Linked List Cycle II | Medium | Phase 2: the cycle start |
| 2095 | Delete the Middle Node of a Linked List | Medium | Stop one node before the middle |
| 143 | Reorder List | Medium | Middle + reversal + merge in one problem |
| 287 | Find the Duplicate Number | Medium | Implicit linked list over array indices |
| 457 | Circular Array Loop | Medium | Cycle detection with direction and length rules |
Problems 234 and 143 also need list reversal; the in-place linked list reversal pattern covers that step. For where this pattern sits among the others, see 15 LeetCode patterns for interviews and the coding interview cheat sheet.
How to practice explaining it
Knowing the template is half the job. In a real round you also have to explain the proof, name the edge cases and state the complexity while someone listens. Practice that out loud: solve one problem from the list, then explain it in under two minutes without looking at your code.
CoPilot Interview's Mock Interview can run a coding round built from your résumé and the job description, read each question aloud and score your spoken answer out of 10 with what to fix and a model answer. If you would rather not install anything, the browser AI mock interview has 113 real questions across 9 tracks, including coding.
Practice explaining Floyd's cycle detection out loud
Run a free AI mock interview in your browser, answer a coding question out loud, and get feedback on how clearly you explained it.
Try a free AI mock interviewFAQ
What is the fast and slow pointers pattern?
It is a technique where two pointers move through the same sequence at different speeds, usually one step and two steps. If the sequence has a cycle they meet inside it; if it does not, the fast pointer reaches the end while the slow pointer sits at the middle. It is also called Floyd's cycle detection or the tortoise and hare algorithm.
Why does the fast pointer move exactly two steps?
With speeds of one and two, the gap inside a cycle shrinks by exactly one node per step, so fast can never skip over slow and they must meet within one lap. Other speeds can work for detection, but one and two keep the code and the cycle-start proof simple.
How do you find where the cycle starts?
After the pointers meet, move one pointer back to the head and advance both one step at a time. The distance from the head to the cycle start equals the distance from the meeting point to the cycle start (modulo the cycle length), so they meet exactly at the start.
What is the time and space complexity of Floyd's cycle detection?
O(n) time and O(1) extra space. Slow enters the cycle within n steps, and the pointers meet within one more lap. The hash-set alternative is also O(n) time but uses O(n) space.
Is fast and slow pointers the same as two pointers?
It is a variant of two pointers. Classic two pointers usually start at opposite ends of a sorted array and move toward each other; fast and slow pointers start together and move in the same direction at different speeds.
Which LeetCode problems use fast and slow pointers?
The core set is Linked List Cycle (141), Linked List Cycle II (142), Middle of the Linked List (876), Happy Number (202), Find the Duplicate Number (287), Palindrome Linked List (234), Reorder List (143) and Circular Array Loop (457).