Overview
The fast and slow pointers technique (Floyd's tortoise and hare) moves two pointers through a sequence at different speeds, usually one step and two steps. If there is a cycle, the fast pointer eventually laps the slow one and they meet. If there is no cycle, the fast pointer reaches the end first.
The same idea finds the middle of a list in one pass (when fast reaches the end, slow is halfway), the start of a cycle, and duplicate numbers in arrays whose values act as next pointers. It uses O(1) extra memory, which is why interviewers love it as a follow-up to hash-set solutions.
Two runners start together, one twice as fast as the other. On a straight road, the fast runner simply finishes first. On a circular track, the fast runner gains one lap-position per step and must eventually catch the slow runner from behind.
When to use it
- Detect a cycle in a linked list or in any sequence x -> f(x).
- Find the middle node, or split a list into halves.
- Find where a cycle begins.
- O(1) space is required where a visited set would otherwise be used.
- Find the kth node from the end (a fixed gap variant).
Problem patterns it solves
Recognize it when: does the list or sequence loop back on itself.
- 141. Linked List Cycle
- 202. Happy Number
- 457. Circular Array Loop
Recognize it when: where does the cycle begin; find the duplicate with O(1) space.
- 142. Linked List Cycle II
- 287. Find the Duplicate Number
Recognize it when: split in half, palindrome check, reorder, sort a list.
- 876. Middle of the Linked List
- 234. Palindrome Linked List
- 143. Reorder List
- 148. Sort List
- 2095. Delete the Middle Node
Recognize it when: kth from the end, remove nth from the end.
- 19. Remove Nth Node From End of List
- 61. Rotate List
- 1721. Swapping Nodes in a Linked List
Where it is used in real software
Iterating a function over states (pseudo-random generators, workflow transitions) can loop; Floyd's algorithm detects it without storing visited states.
This integer factorization algorithm uses Floyd's cycle detection on a pseudo-random sequence modulo n.
Memory debuggers and garbage collectors guard against cyclic corruption in linked lists.
Splitting a singly linked sequence without knowing its length is a standard step in merge-sorting linked data.
Key terms
- Tortoise (slow)
- Moves one step per iteration.
- Hare (fast)
- Moves two steps per iteration.
- Meeting point
- Where slow and fast coincide inside a cycle.
- Cycle entry
- First node of the cycle; found by resetting one pointer to the head.
- Functional graph
- Each value points to exactly one next value, e.g. i -> nums[i].
Floyd's algorithm for the cycle start
- 1Phase 1: detect
slow = slow.next, fast = fast.next.next until they meet (cycle) or fast reaches null (no cycle).
- 2Phase 2: reset
Move one pointer back to the head. Keep the other at the meeting point.
- 3Move both one step at a time
They meet exactly at the cycle entry.
- 4Why it works
If the distance from head to the entry is a and the meeting point is b steps into a cycle of length c, then 2(a + b) = a + b + kc, so a = kc - b. Walking a steps from the meeting point lands on the entry.
STEP 1Both pointers start at the head.
Find the duplicate number with O(1) space
nums = [1, 3, 4, 2, 2]; treat index -> nums[index] as a next pointer
| Phase | slow | fast | Note |
|---|---|---|---|
| Start | nums[0] = 1 | nums[0] = 1 | both start at value 1 |
| Detect 1 | nums[1] = 3 | nums[nums[1]] = nums[3] = 2 | slow 1 step, fast 2 steps |
| Detect 2 | nums[3] = 2 | nums[nums[2]] = nums[4] = 2 | meet at 2 (inside the cycle) |
| Reset | nums[0] = 1 | 2 | slow back to the start |
| Find 1 | nums[1] = 3 | nums[2] = 4 | both move 1 step |
| Find 2 | nums[3] = 2 | nums[4] = 2 | meet at 2: the duplicate |
NOWPhase: Start | slow: nums[0] = 1 | fast: nums[0] = 1 | Note: both start at value 1
The duplicate value is where two indexes point, which is the entry of the cycle. O(n) time and O(1) space, without modifying the array.
Implementation
function hasCycle(head) { let slow = head, fast = head; while (fast && fast.next) { slow = slow.next; fast = fast.next.next; if (slow === fast) return true; } return false;} function detectCycle(head) { let slow = head, fast = head; while (fast && fast.next) { slow = slow.next; fast = fast.next.next; if (slow === fast) { slow = head; // phase 2 while (slow !== fast) { slow = slow.next; fast = fast.next; } return slow; } } return null;} function middleNode(head) { let slow = head, fast = head; while (fast && fast.next) { slow = slow.next; fast = fast.next.next; } return slow; // second middle for even lengths} function findDuplicate(nums) { let slow = nums[0], fast = nums[0]; do { slow = nums[slow]; fast = nums[nums[fast]]; } while (slow !== fast); slow = nums[0]; while (slow !== fast) { slow = nums[slow]; fast = nums[fast]; } return slow;}Complexity and performance
Fast catches slow within one lap of the cycle.
Two pointers only.
Simpler but uses memory.
Trade-offs
A visited set is easier to write and also gives the entry node directly, but costs O(n) memory.
Palindrome checks reverse the second half; restore it afterward if the caller needs the original list.
Variants and related techniques
Starting fast at head.next returns the first middle for even-length lists, which is what merge sort splitting needs.
A faster cycle detection using powers of two; same O(1) space.
After meeting, keep one pointer fixed and count steps until the other returns.
Common mistakes
- Checking only fast.next.
Fix: The loop condition must be fast && fast.next, in that order.
- Comparing values instead of node references.
Fix: Use slow === fast; different nodes can hold equal values.
- Wrong start in findDuplicate.
Fix: Start both at nums[0] and use a do-while so they move before comparing.
Interview questions
Why must the fast pointer meet the slow pointer in a cycle?
Once both are inside the cycle, the gap between them shrinks by one each step (fast gains one position per step), so it reaches zero within one cycle length.
Why does resetting one pointer to the head find the cycle entry?
The head-to-entry distance equals the meeting-point-to-entry distance modulo the cycle length, so both pointers reach the entry at the same time.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| 876. Middle of the Linked List | Easy | Basic speeds. |
| 141. Linked List Cycle | Easy | Detection. |
| 142. Linked List Cycle II | Medium | Entry point. |
| 19. Remove Nth Node From End of List | Medium | Fixed gap. |
| 143. Reorder List | Medium | Middle, reverse, merge. |
| 287. Find the Duplicate Number | Medium | Array as a linked list. |