106. Find Cycle Entry
Problem
Return the node where a reachable next-link cycle begins, or None if the chain is acyclic. Values may repeat. Do not change links or use a visited set. The cycle entry is the first cycle node reached from head.
Examples
Example 1
head = build_list([7,7,7]); head.next.next.next = head.next
cycle_entry(head) is head.next
=> TrueThe returned reference is the entry node, even with duplicate values.
Example 2
cycle_entry(build_list([1,2]))
=> NoneThe fast pointer reaches the end.
Example 3
head = build_list([9]); head.next = head
cycle_entry(head) is head
=> TrueA self-loop begins at the head.
Reveal the worked solution and reasoning
Why this mechanism works
First find a slow/fast meeting. If the noncyclic prefix length is mu and cycle length is lambda, the meeting relation implies mu plus the meeting's cycle offset is a multiple of lambda. Moving one pointer from head and one from the meeting at equal speed therefore makes them meet at the entry.
State and boundaries
The first phase locates any meeting in the cycle. In the second, entry starts at head while slow remains at the meeting; both move one link per step.
Protocol
- Run guarded slow/fast movement until meeting or exhaustion.
- Return None if there is no meeting.
- Reset one pointer to head.
- Advance both one step until identities match.
- Return that node.
Visualization
head -- mu links --> entry -- cycle offset --> meeting
meeting pointer + mu steps wraps to entry
head pointer + mu steps reaches entryPython implementation
from structures import build_list
def cycle_entry(head):
slow = fast = head
while fast is not None and fast.next is not None:
slow = slow.next
fast = fast.next.next
if slow is fast:
break
else:
return None
entry = head
while entry is not slow:
entry = entry.next
slow = slow.next
return entry
Worked execution
cycle_entry(head) is head.next
L4 cycle_entry: slow = fast = head head=N1(val=7,next=N2,random=None); slow=N1(val=7,next=N2,random=None); fast=N1(val=7,next=N2,random=None) L6 cycle_entry: slow = slow.next head=N1(val=7,next=N2,random=None); slow=N2(val=7,next=N3,random=None); fast=N1(val=7,next=N2,random=None) L7 cycle_entry: fast = fast.next.next head=N1(val=7,next=N2,random=None); slow=N2(val=7,next=N3,random=None); fast=N3(val=7,next=N2,random=None) L6 cycle_entry: slow = slow.next head=N1(val=7,next=N2,random=None); slow=N3(val=7,next=N2,random=None); fast=N3(val=7,next=N2,random=None) L12 cycle_entry: entry = head head=N1(val=7,next=N2,random=None); slow=N3(val=7,next=N2,random=None); fast=N3(val=7,next=N2,random=None); entry=N1(val=7,next=N2,random=None) L14 cycle_entry: entry = entry.next head=N1(val=7,next=N2,random=None); slow=N3(val=7,next=N2,random=None); fast=N3(val=7,next=N2,random=None); entry=N2(val=7,next=N3,random=None) L15 cycle_entry: slow = slow.next head=N1(val=7,next=N2,random=None); slow=N2(val=7,next=N3,random=None); fast=N3(val=7,next=N2,random=None); entry=N2(val=7,next=N3,random=None)
Result: True. The returned reference is the entry node, even with duplicate values.
cycle_entry(build_list([1,2]))
L4 cycle_entry: slow = fast = head head=N1(val=1,next=N2,random=None); slow=N1(val=1,next=N2,random=None); fast=N1(val=1,next=N2,random=None) L6 cycle_entry: slow = slow.next head=N1(val=1,next=N2,random=None); slow=N2(val=2,next=None,random=None); fast=N1(val=1,next=N2,random=None) L7 cycle_entry: fast = fast.next.next head=N1(val=1,next=N2,random=None); slow=N2(val=2,next=None,random=None); fast=None
Result: None. The fast pointer reaches the end.
Why the solution is correct
At the first meeting, the fast-minus-slow distance is a multiple of the cycle length, yielding the modular relation for the entry offset. After mu equal-speed steps, the head pointer reaches the entry and the meeting pointer reaches the same cycle position. Before then, the head pointer is outside the cycle, so this is their first match.
Time and space
O(n) time and O(1) auxiliary space.
Common mistakes
Resetting both pointers to head loses the needed meeting offset. Returning the first meeting node is not generally returning the cycle entry.
Reasoning card
State: The first phase locates any meeting in the cycle. In the second, entry starts at head while slow remains at the meeting; both move one link per step.
Invariant: Equal-distance reasoning makes the next meeting the entry.
Decision to rehearse: After a meeting, move one pointer to head and advance both one step.
Finish: Return that node
Transfer exercise
Derive an alternative entry search using the measured cycle length as a fixed gap between two pointers.