The problem
You get the head of a singly linked list. Some node’s next may point back to an earlier node, which makes the list loop forever. Return True if the list has a cycle, False if it ends.
Input: 3 -> 2 -> 0 -> -4
^ |
+---------+ (the tail -4 points back to node 2)
Output: TrueFollowing next from -4 goes back to 2, so you never reach the end.
How to think about it
Start with brute force. Walk the list and store every node you visit in a set. If you ever see a node twice, there is a cycle. If you reach None, there is not. That is O(n) time but O(n) extra space, and the follow-up always asks for O(1).
You cannot just walk and wait. Without memory, a single pointer in a cycle runs forever and never “knows” it is looping. You need something that turns “loops forever” into a condition you can test.
The key insight: two runners on a circular track must meet. Move s one step and f two steps per tick. If the list ends, f hits None first and you return False. If there is a cycle, both pointers eventually enter it. Once inside, f gains exactly one node per tick on s, so the gap between them shrinks by one each tick and must reach zero. They land on the same node, and that collision is your proof of a cycle.
Which pattern is this? Fast & slow pointers (Floyd’s tortoise and hare). The tell is “detect a loop in a sequence with O(1) memory”.
Approach
- Start
sandfathead. - While
fandf.nextexist, movesone step andftwo steps. - After each move, if
s is f(the same node object), returnTrue. - If the loop exits,
freached the end, so returnFalse.
Solution
def has_cycle(head):
s = f = head
while f and f.next: # fast hit the end -> no cycle
s = s.next; f = f.next.next
if s is f: return True # fast lapped slow -> cycle
return False
# has_cycle([3,2,0,-4] with tail linked to node index 1) -> TrueDry run
On the example, the cycle is 2 -> 0 -> -4 -> 2. Values shown are the nodes each pointer sits on after the step:
| Step | s |
f |
s is f? |
|---|---|---|---|
| start | 3 | 3 | (not checked) |
| 1 | 2 | 0 | no |
| 2 | 0 | 2 | no |
| 3 | -4 | -4 | yes |
They meet on node -4 after three steps, so the answer is True. On 1 -> 2 with no cycle, after one step f is None and the function returns False.
Complexity
- Time: O(n). Before the cycle, slow takes at most n steps. Inside the cycle, the gap closes by one per tick, so fast catches slow within one lap.
- Space: O(1). Two pointers, no visited set.
Edge cases and pitfalls
- Empty list or a single node with no cycle: the loop condition fails at once and returns
False. - A single node pointing to itself: after one step both pointers are on it, so it returns
True. - Compare nodes, not values. Use
is. Two different nodes can hold the same value, ands.val == f.valwould report false cycles. - Check after moving, not before. Both pointers start on
head, so checkings is ffirst would returnTrueimmediately. - Null safety.
f.next.nextneeds bothfandf.nextto exist, which is why the loop guards both.
Related problems
- Linked List Cycle II (LeetCode 142): after the meeting, find where the cycle starts.
- Happy Number (LeetCode 202): the same detection on a sequence of numbers instead of nodes.
- Find the Duplicate Number (LeetCode 287): an array disguised as a linked list with a cycle.
- 1.Middle of the Linked List (LeetCode 876)
- 2.Linked List Cycle (LeetCode 141)
- 3.Happy Number (LeetCode 202)
- 4.Palindrome Linked List (LeetCode 234)
- 5.Intersection of Two Linked Lists (LeetCode 160)
- 6.Linked List Cycle II (LeetCode 142)
- 7.Find the Duplicate Number (LeetCode 287)
- 8.Remove Nth Node From End of List (LeetCode 19)
- 9.Reorder List (LeetCode 143)
- 10.Circular Array Loop (LeetCode 457)