LeetCode #141 Easy

Linked List Cycle

Return true if the linked list contains a cycle.

linked-listtwo-pointersfloyd
Open on LeetCode ↗
02

Intuition

Race a slow pointer (one step) against a fast pointer (two steps). With no cycle, fast runs off the end. With a cycle, fast can never escape and steadily laps slow until they collide.

How to spot this pattern

Floyd's tortoise and hare. If a cycle exists the fast pointer laps the slow one and they must meet; if not, fast simply falls off the end. The reason they can't skip past each other is that the gap shrinks by exactly one each step — that argument is what makes O(1) space possible where a hash set would need O(n).

03

Approach

1

The hash-set answer costs memory

You can store every node you visit in a set and declare a cycle the moment you revisit one. It's simple and O(n) time, but it's O(n) space. The elegant solution gets the same answer with two pointers and no extra memory — Floyd's tortoise and hare.

2

A fast runner must lap a slow one inside a loop

Run slow one step at a time and fast two steps at a time. If the list ends, fast (or fast.next) hits null and there's clearly no cycle. But if there is a cycle, fast can never escape it; once both pointers are inside the loop, fast gains exactly one node on slow every step, so the gap shrinks to zero and they must collide. A collision is therefore proof of a cycle.

3

Step, then compare

While fast and fast.next exist, advance slow by 1 and fast by 2, then check whether they point to the same node (identity, not value). If so, return true; if fast runs off the end, return false. O(n) time, O(1) space.

04

Solution & live demo

1class Solution:
2 def hasCycle(self, head):
3 slow = fast = head
4 while fast and fast.next:
5 slow = slow.next
6 fast = fast.next.next
7 if slow is fast:
8 return True
9 return False
05

Common pitfalls

Comparing values instead of identity

✗ Wrong
if slow.val == fast.val: return True
✓ Right
if slow is fast: return True

Two distinct nodes can easily hold the same value in a perfectly acyclic list, producing a false positive. A cycle means the pointers reach the same node, which is an identity question.

Advancing both by one

✗ Wrong
slow = slow.next
fast = fast.next
✓ Right
slow = slow.next
fast = fast.next.next

Equal speeds keep the gap constant forever, so they never meet inside a cycle and the loop runs indefinitely. The difference in speed is the entire mechanism.

Checking for a meeting before moving

✗ Wrong
while fast and fast.next:
    if slow is fast: return True
    slow = slow.next
    fast = fast.next.next
✓ Right
    slow = slow.next
    fast = fast.next.next
    if slow is fast: return True

Both start at head, so testing before the first move reports a cycle on every non-empty list. The comparison belongs after the advance.

06

Edge cases

No cycle

fast or fast.next becomes null and the loop exits with false.

Cycle back to the head

The pointers still meet inside the loop; identity (is) compares nodes, not values.

Empty or single node without a loop

The loop condition is false immediately, returning false.

07

Complexity

Time
O(n)
Space
O(1)
Two pointers; slow visits each node at most once.