Pattern #03
Fast & Slow Pointers
EssentialLinked-list cycles, middle nodes, and fixed pointer gaps.
When to use
Prefer on linked lists (or cyclic sequences) when you need the middle, detect a cycle, or maintain a gap of k nodes. without knowing the length up front.
Recognition cues
- Linked list cycle detection
- Find middle of list
- Remove nth node from end (gap of n)
- Palindrome linked list (find mid, reverse half)
Common pitfalls
- Null checks on fast.next before fast.next.next
- Off-by-one when positioning the gap for "nth from end"
- Forgetting to reconnect head when deleting the first node
90-second recognition drill
Which pattern fits best?
- Linked list cycle detection
- Find middle of list
- Remove nth node from end (gap of n)
Interactive
Mental model
A full worked walkthrough of the invariant. Pause, scrub the dots, or use ← →. Aim to narrate each step yourself.
Slow steps +1, fast steps +2. No need to know the list length.
How to think about it
Two pointers move at different speeds (or with a fixed offset). When the fast pointer finishes, slow is at a useful position (middle, cycle entry after reset, etc.). No need to know the list length up front.
Classic results
- Cycle: if they meet, a cycle exists (Floyd).
- Middle: when fast hits the end, slow is at mid.
- Nth from end: advance fast by n, then move both until fast ends.
Complexity baseline
O(n) time, O(1) extra space. the main reason to prefer this over storing the list.
Template
Same skeleton in TypeScript, Python, and C#. Adapt the invariant; keep the structure.
/** Fast/slow template: detect cycle (Floyd). */
export class ListNode {
val: number;
next: ListNode | null;
constructor(val = 0, next: ListNode | null = null) {
this.val = val; this.next = next;
}
}
export function hasCycle(head: ListNode | null): boolean {
let slow = head, fast = head;
while (fast?.next) {
slow = slow!.next;
fast = fast.next.next;
if (slow === fast) return true;
}
return false;
}
/** Fast/slow template: detect cycle (Floyd). */
export class ListNode {
val: number;
next: ListNode | null;
constructor(val = 0, next: ListNode | null = null) {
this.val = val; this.next = next;
}
}
export function hasCycle(head: ListNode | null): boolean {
let slow = head, fast = head;
while (fast?.next) {
slow = slow!.next;
fast = fast.next.next;
if (slow === fast) return true;
}
return false;
}
- 1#19 Remove Nth Node From End of ListGuidemedium
- 2#141 Linked List CycleGuideeasy
- 3#142 Linked List Cycle IIGuidemedium
- 4#160 Intersection of Two Linked ListsGuideeasy
- 5#234 Palindrome Linked ListGuideeasy
- 6#876 Middle of the Linked ListGuideeasy