Skip to content
ΣDSA Patterns
Menu
Language

Pattern #03

Fast & Slow Pointers

Essential

Linked-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.

Step 1 of 8
1
2
3
4
5

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 Pointers · Template
/** 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;
}