Fast & Slow Pointers
A pattern that moves two pointers through the same linked list at different speeds - one hop vs two - to find the middle or detect a cycle in a single O(n) pass with no extra memory.
The input is a linked list and you need to find the middle node, detect a cycle, or find where a cycle begins - without storing visited nodes. The speed difference is the entire trick.
The speed difference beats the naive set approach without extra memory
Fast and slow pointers matters because it solves linked-list problems in one pass without remembering every node you have seen. The speed gap is not just an implementation detail - it is the proof.
| Aspect | Naive (HashSet) | Fast & Slow |
|---|---|---|
| Cycle detection | Store every visited node in a set, check membership on each step | Two pointers - if they ever meet, a cycle exists |
| Time | O(n) | O(n) |
| Space | O(n) - stores every node | O(1) - just two pointers |
| Why it matters | Works, but memory grows with list size | Memory is constant regardless of list size - no set needed |
If a cycle exists, the fast pointer laps the slow pointer inside the loop - they must eventually land on the same node. That is the entire proof.
See the mechanic, then the reason behind it
The card below uses the same structure as the two-pointers page: tabs, progress dots, step controls, side-by-side What / Why insight, and a fixed code panel. The linked-list motion is the only thing that changes.
What
Both pointers start at head (node 1).
Why
Slow will cover half the distance fast does. When fast hits the end, slow is exactly at the middle - no counting needed.
Code For This Pattern
function findMiddle(head) {
let slow = head;
let fast = head;
while (fast !== null && fast.next !== null) {
slow = slow.next; // one hop
fast = fast.next.next; // two hops
}
return slow; // slow is at the middle
}One speed difference solves two classic linked-list problems
The loop looks almost identical in both problems. The difference is what you are waiting for: null in one case, a meeting point in the other.
| Aspect | Find Middle | Detect Cycle |
|---|---|---|
| Goal | Find the midpoint without counting | Detect whether next pointers loop back |
| Key moment | When fast reaches null | When slow and fast land on the same node |
| Why it works | Fast covers 2x the distance, so slow is halfway when fast finishes | Inside a cycle, fast gains 1 node per step on slow - it must catch up |
| Return value | slow (the middle node) | true / false |
| Complexity | O(n) time, O(1) space | O(n) time, O(1) space |
The short version
- The entire pattern is one insight: fast covers twice the distance of slow in the same number of steps.
- Find Middle: when fast hits null, slow is at the exact midpoint - no length count needed.
- Detect Cycle: inside any loop, a pointer moving 2x faster than another must eventually lap it. Meeting = cycle.
- Both patterns use O(1) space - no array, no set, no visited map. Just two pointers.
- If the list is null or has one node, fast hits null immediately - handle that edge case first.