HARD Lab · Linked List · 54

141. Linked List Cycle

Detect whether a linked list contains a cycle using Floyd's tortoise-and-hare algorithm — two pointers moving at different speeds will inevitably meet inside any cycle, requiring O(1) extra space and a single pass.

EasyFast & Slow PointersTypeScript

PROBLEM What we're solving

Given the head of a singly-linked list, return trueif the list contains a cycle (some node's next pointer eventually leads back to a previously-visited node) and false otherwise. Example: list 3 → 2 → 0 → -4 → (back to 2)true. List 1 → 2 → nullfalse.

KEY IDEA Two speeds always meet in a loop

Insight → put a slow pointer (advances 1 node) and a fast pointer (advances 2 nodes) at the head. If there is a cycle, the fast pointer wraps around and catches up to the slow pointer — they must meet. If there is no cycle, fast hits null first. No extra memory beyond two pointers.

RECIPE Tortoise and hare — step by step

  • 0 · Init. Set slow = head and fast = head. Both start at the same node.
  • 1 · Guard. Loop while fast !== null && fast.next !== null. If either is null, there is no cycle — a null pointer can't wrap back.
  • 2 · Advance. Move slow = slow.next (1 step) and fast = fast.next.next (2 steps). Fast gains on slow by exactly 1 node per iteration inside a cycle.
  • 3 · Check meeting. If slow === fast (same node object, not just same value), a cycle exists — return true.
  • 4 · Exhausted. Loop ended with fast hitting null — no cycle. Return false.
Classic confusion → the check is reference equality (slow === fast), not value equality. Two different nodes can hold the same integer value; only the same node object proves a cycle.

COST Complexity & trade-offs

Hash set of visited nodes
O(n) space
O(n) time but stores every visited pointer.
Fast & slow pointers
O(1) space
O(n) time; only two pointer variables needed.

Why fast catches slow

Once both pointers are in the cycle, think of fast's lead over slow decreasing by 1 each step (in the cycle's frame of reference). Because the cycle is finite, the lead reaches 0 — they meet — within at most c steps (cycle length).

Pattern transfer → fast & slow pointers extend to Linked List Cycle II (find cycle entry), Find the Duplicate Number (array as implicit list), Middle of the Linked List, and Happy Number.

RUN IT Floyd's tortoise and hare — detect a cycle

step 0 / 3
STARTList has 4 nodes. Tail links back to index 1 (cycle). Place slow and fast at head (index 0).
3S+F2102-43
State
idx 0
slow
idx 0
fast
slow pointerfast pointerpointers meet (cycle!)fast → null (no cycle)
slowfast

TYPESCRIPT The solution, annotated

hasCycle.ts
// Definition for singly-linked list.
// class ListNode {
//   val: number;
//   next: ListNode | null;
//   constructor(val?: number, next?: ListNode | null) {
//     this.val = val ?? 0;
//     this.next = next ?? null;
//   }
// }

function hasCycle(head: ListNode | null): boolean {
  let slow: ListNode | null = head;
  let fast: ListNode | null = head;

  while (fast !== null && fast.next !== null) {
    slow = slow!.next;          // advance 1
    fast = fast.next.next;      // advance 2

    if (slow === fast) return true; // pointers met — cycle
  }

  return false; // fast fell off end — no cycle
}

Reading it block by block

Lines 14–15 — initialise both pointers. Both slow and fast begin at head. Starting them at the same node is safe; the first iteration separates them before the equality check.
Line 17 — loop guard. We check fast !== null && fast.next !== null before advancing. If fast is null or its next is null, the list terminates — no cycle possible. We never dereference a null pointer.
Lines 18–19 — advance. slow moves one step; fastmoves two. Inside a cycle, fast gains one node on slow per iteration, guaranteeing they meet within the cycle's length.
Line 21 — meeting check. slow === fast is a reference comparison — same node object. When they point to the same node, a cycle is confirmed.
Line 25 — no cycle. Exiting the loop means fast (or fast.next) was null — the list has a proper end. Return false.
Complexity → O(n) time — fast pointer traverses at most n + cycle-length steps before meeting slow or reaching null. O(1) space — only two pointer variables.

INTERVIEWFollow-ups they'll ask

  • "Find the start of the cycle?" After the meeting point, reset one pointer to head and advance both one step at a time — they meet at the cycle entry (LC 142).
  • "Can you solve it with a hash set?" Yes — store each visited node; return true on first repeat. Simpler code but O(n) space vs O(1) for fast/slow.
  • "What if fast starts two nodes ahead of slow?" Still works — the gap modulo the cycle length still shrinks to zero.
  • "Happy Number (LC 202)?"Repeatedly replacing a number with the sum of squares of its digits forms an implicit linked list; fast & slow detects the cycle.
  • "Middle of the Linked List (LC 876)?" When fast reaches the end, slow is at the middle — same two-pointer setup.

MNEMONIC The one-liner

"Tortoise walks, hare runs — inside a loop they must collide; on a straight road the hare escapes."

TRIGGERS When you see ___ → reach for ___

detect a cycle in a linked listfast & slow pointers (Floyd)
O(1) space cycle / loop detectiontwo-pointer speed difference
"does the sequence revisit a state?"implicit list + fast/slow
find the middle of a linked listfast runs 2×; slow lands at mid

SKELETON The reusable shape

skeleton.ts
function hasCycle(head: ListNode | null): boolean {
  let slow = head;
  let fast = head;

  while (fast !== null && fast.next !== null) {
    slow = slow!.next;
    fast = fast.next.next;
    if (slow === fast) return true;
  }

  return false;
}

FLASHCARDS Tap to flip

Floyd's algorithm: what are the two pointer speeds?
Slow advances 1 node per step; fast advances 2 nodes per step.
tap to flip
1 / 7
Answer to reveal explanations. No penalty for retries.Score: 0 / 7
QUESTION 01
What is the space complexity of the fast & slow pointer approach?
QUESTION 02
The loop condition is fast !== null && fast.next !== null. Why must both be checked?
QUESTION 03
Two nodes have value 5. When slow and fast both point to one of them, does that confirm a cycle?
QUESTION 04
Inside a cycle of length c, how many iterations does it take for fast to catch slow after both are in the cycle?
QUESTION 05
What should slow and fast both be initialised to?
QUESTION 06
Which of the following problems is also solved with a fast & slow pointer?
QUESTION 07
If the input list is empty (head = null), what does the function return?