Linked List Cycle II medium
Problem Statement
Given the head
of a linked list, return the node where the cycle begins. If there is no cycle, return None
.
There is a cycle in a linked list if there is some node in the list that can be reached again by continuously following the next
pointer. Internally, pos
is used to denote the index of the node that tail's next
pointer is connected to (0-indexed). It is -1 if there is no cycle. Note that pos
is not passed as a parameter.
Constraints:
- The number of the nodes in the list is in the range [0, 104].
- -105 <= Node.val <= 105
pos
is -1 or a valid index in the linked-list.
Example 1
Input: head = [3,2,0,-4], pos = 1 Output: tail connects to node index 1 Explanation: There is a cycle in the linked list, where tail connects to the second node.
Example 2
Input: head = [1,2], pos = 0 Output: tail connects to node index 0 Explanation: There is a cycle in the linked list, where tail connects to the first node.
Example 3
Input: head = [1], pos = -1 Output: None Explanation: There is no cycle in the linked list.
Steps and Explanation
The problem can be solved using Floyd's Tortoise and Hare algorithm. This algorithm consists of two pointers: a "slow" pointer (tortoise) and a "fast" pointer (hare).
-
Cycle Detection: The slow pointer moves one step at a time, while the fast pointer moves two steps at a time. If there is a cycle, the fast pointer will eventually lap the slow pointer. If they meet, a cycle exists.
-
Finding the Cycle Start: Once the cycle is detected, reset the slow pointer to the head of the list. Keep the fast pointer where it is (at the meeting point). Now, move both pointers one step at a time. The point where they meet again is the start of the cycle.
Why this works:
When the slow and fast pointers meet, let's say the slow pointer has traveled x
steps and the fast pointer has traveled 2x
steps. Let y
be the distance from the start of the cycle to the meeting point. Let c
be the length of the cycle. Then we have:
x = a + y
(wherea
is the distance from the head to the start of the cycle)2x = a + y + nc
(wheren
is the number of times the fast pointer has gone around the cycle)
Subtracting the first equation from the second:
x = nc - y
This means that the distance from the meeting point to the start of the cycle is y
, which is also the distance from the head to the start of the cycle. Therefore, by moving both pointers at the same speed from the head and the meeting point, they will meet at the start of the cycle.
Code
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
def detectCycle(head):
"""
Finds the node where the cycle begins in a linked list.
Args:
head: The head of the linked list.
Returns:
The node where the cycle begins, or None if there is no cycle.
"""
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast: # Cycle detected
slow = head
while slow != fast:
slow = slow.next
fast = fast.next
return slow # slow (and fast) now points to the cycle start
return None # No cycle
#Example usage
head = ListNode(3)
head.next = ListNode(2)
head.next.next = ListNode(0)
head.next.next.next = ListNode(-4)
head.next.next.next.next = head.next #create cycle
cycle_start = detectCycle(head)
if cycle_start:
print(f"Cycle starts at node with value: {cycle_start.val}")
else:
print("No cycle detected")
head2 = ListNode(1)
head2.next = ListNode(2)
cycle_start2 = detectCycle(head2)
if cycle_start2:
print(f"Cycle starts at node with value: {cycle_start2.val}")
else:
print("No cycle detected")
Complexity
- Time Complexity: O(N), where N is the number of nodes in the linked list. We traverse the list at most twice.
- Space Complexity: O(1). We use only a constant amount of extra space for the pointers.