The problem
You get the head of a singly linked list that may contain a cycle. Return the node where the cycle starts (the first node you visit twice when walking from the head), or None if there is no cycle. Don’t modify the list.
Input: 1 -> 2 -> 3 -> 4
^ |
+---------+ (the tail 4 points back to node 2)
Output: the node with value 2Walking from 1 you go 2, 3, 4, then back to 2. Node 2 is the entrance to the loop.
How to think about it
Start with brute force. Walk the list, storing each node in a set. The first node you see a second time is the entrance. That is O(n) time and O(n) space. The follow-up asks for O(1).
Step one is the familiar part. Run tortoise and hare exactly as in Linked List Cycle: s moves one step, f moves two. If f reaches the end, there is no cycle. If they meet, there is one. But the meeting point is somewhere inside the loop, not necessarily the entrance.
The key insight: the head and the meeting point are the same distance from the entrance. Let a be the steps from the head to the entrance, b the steps from the entrance to the meeting point, and c the cycle length. When they meet, slow has walked a + b and fast has walked twice that. Fast’s extra distance is whole laps, so a + b = k·c for some k, which rearranges to a = k·c − b. From the meeting point, walking k·c − b steps brings you round to the entrance. So if you start a new pointer at the head and step it and slow one node at a time, they meet exactly at the entrance.
Which pattern is this? Fast & slow pointers, plus the head-reset trick. The tell is “find where a cycle starts in O(1) space”.
Approach
- Move
sone step andftwo steps whilefandf.nextexist. - If the loop ends without them meeting, return
None. - When
s is f, place a new pointerpathead. - Move
pandsone step at a time until they are the same node. - Return
p: the cycle’s entrance.
Solution
def detect_cycle_start(head):
s = f = head
while f and f.next:
s = s.next; f = f.next.next
if s is f: # met inside the cycle
p = head # head is a = k*c - b from the entrance
while p is not s: p = p.next; s = s.next # both one step: meet at the entrance
return p
return None
# list [1,2,3,4] with tail linked to node val 2 -> node with val 2Dry run
On the example (a = 1, c = 3):
| Phase | s |
f / p |
Note |
|---|---|---|---|
| step 1 | 2 | f = 3 | |
| step 2 | 3 | f = 2 | fast has wrapped round |
| step 3 | 4 | f = 4 | meet at node 4 (b = 2) |
| reset | 4 | p = 1 | p starts at the head |
| walk 1 | 2 | p = 2 | p is s: entrance found |
It returns the node with value 2. Check the formula: a = k·c − b = 3 − 2 = 1, and indeed the entrance is one step from the head.
Complexity
- Time: O(n). Finding the meeting point takes O(n), as in cycle detection. The second walk takes
asteps, which is at most n. - Space: O(1). Three pointers.
Edge cases and pitfalls
- No cycle:
freaches the end and the function returnsNone. - Cycle starts at the head (a = 0): the meeting point is
k·csteps in, sop is simmediately after the reset and it returnshead. - A single node pointing to itself: both pointers meet on it after one step and it returns that node.
- Moving fast two steps in phase two is a common slip. Both pointers must move one step after the reset.
- Returning the meeting point is the wrong answer in general. It equals the entrance only by coincidence.
Related problems
- Linked List Cycle (LeetCode 141): phase one on its own.
- Find the Duplicate Number (LeetCode 287): this exact algorithm, where the entrance is the duplicate.
- Intersection of Two Linked Lists (LeetCode 160): another equal-distance trick to make two pointers meet.
- 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)