Intuition
Walk the list one node at a time. If you ever encounter a node you've already seen, you've looped back — there's a cycle. A set of node references (not values) is the natural "seen" tracker.
Explanation
Why we store references, not values
Two distinct nodes can hold the same value. If we stored values, we'd report a cycle whenever a duplicate value appeared. We need to detect that we've revisited the exact same node object. Python's set hashes objects by identity by default for custom classes, which is exactly what we want.
Steps
- Initialise
seen = set(). - While
headis notNone:- If
headis already inseen, returnTrue— we've revisited a node, so there's a cycle. - Otherwise add
headtoseenand advancehead = head.next.
- If
- If we fall off the end (
head is None), there was no cycle — returnFalse.
Walk-through
Cyclic case: 1 → 2 → 3 → 4 → 5 → 3 (back). The next of node 5 points back to node 3.
| step | head val | head in seen? | seen after |
|---|---|---|---|
| 1 | 1 | no | {n1} |
| 2 | 2 | no | {n1, n2} |
| 3 | 3 | no | {n1, n2, n3} |
| 4 | 4 | no | {n1, n2, n3, n4} |
| 5 | 5 | no | {n1, n2, n3, n4, n5} |
| 6 | 3 (revisit) | yes | — return True ✓ |
Acyclic case: 1 → 2 → 3 → None. The loop visits each node once, never finds a repeat, and head becomes None after step 3 — return False.
Edge cases
- Empty list: the
whiledoesn't enter; returnFalse. - Single node, no cycle: one iteration, then
head = None, returnFalse. - Single node looping to itself:
headis added toseen, thenhead.next = headrevisits, returnTrue.
Complexity
- Time:
O(n)— at mostninsertions and lookups before either revisiting or hittingNone. - Space:
O(n)— up tonreferences in the set.
Implementation
import java.util.*;
class Solution {
static class ListNode {
int val;
ListNode next;
ListNode(int v) { val = v; }
}
public boolean hasCycle(ListNode head) {
Set<ListNode> seen = new HashSet<>();
while (head != null) {
if (seen.contains(head)) return true;
seen.add(head);
head = head.next;
}
return false;
}
}