Given head
, the head of a linked list, determine if the linked list has a cycle in it.
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. Note that pos
is not passed as a parameter.
Return true
if there is a cycle in the linked list. Otherwise, return false
.
Example 1:
Input: head = [3,2,0,-4], pos = 1 Output: true Explanation: There is a cycle in the linked list, where the tail connects to the 1st node (0-indexed).
Example 2:
Input: head = [1,2], pos = 0 Output: true Explanation: There is a cycle in the linked list, where the tail connects to the 0th node.
Example 3:
Input: head = [1], pos = -1 Output: false Explanation: There is no cycle in the linked list.
Constraints:
[0, 104]
.-105 <= Node.val <= 105
pos
is -1
or a valid index in the linked-list.Follow up: Can you solve it using O(1)
(i.e. constant) memory?
When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
Think of the linked list as a train. We want to find out if the train is going in circles. The brute force approach is like checking if we've seen the same car go by before by remembering every car we've seen.
Here's how the algorithm would work step-by-step:
def has_cycle_brute_force(head):
nodes_seen = set()
current_node = head
while current_node:
# If we've seen the node before, there's a cycle.
if current_node in nodes_seen:
return True
# Store the node in memory.
nodes_seen.add(current_node)
current_node = current_node.next
# If we reach the end without finding a cycle, return False.
return False
The problem is about detecting if a linked list has a cycle (loop). The optimal approach uses two pointers that move at different speeds to detect this cycle without needing extra memory.
Here's how the algorithm would work step-by-step:
def has_cycle(head):
if not head:
return False
slow_pointer = head
fast_pointer = head
while fast_pointer and fast_pointer.next:
slow_pointer = slow_pointer.next
fast_pointer = fast_pointer.next.next
# Detect the cycle. If they meet, there's a cycle
if slow_pointer == fast_pointer:
return True
# If fast reaches the end, no cycle
return False
Case | How to Handle |
---|---|
Empty list (head is null) | Return false immediately since an empty list cannot have a cycle. |
List with only one node (head.next is null) | Return false since a single node cannot form a cycle. |
List with two nodes and a cycle (head.next points back to head) | Floyd's algorithm (tortoise and hare) correctly detects this case. |
Large list with a cycle far from the head | Floyd's algorithm guarantees detection within a reasonable number of steps. |
Very long list with no cycle; potential for slow execution | Floyd's algorithm will terminate when the fast pointer reaches the end of the list (null). |
List with a cycle that includes the head node | Floyd's algorithm will detect this cycle as the pointers will eventually meet. |
List where cycle occurs very close to the tail. | The fast pointer will eventually catch up to the slow pointer within the cycle. |
The list contains a self-loop (node points to itself). | Floyd's algorithm will correctly detect this as a cycle since the pointers will immediately meet. |