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 <= 105pos 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:
The brute force strategy is like leaving a breadcrumb on every stone you step on as you walk down a path. If you ever come across a stone that already has a breadcrumb, you know you've been walking in a circle.
Here's how the algorithm would work step-by-step:
class ListNode:
def __init__(self, value=0, next_node=None):
self.val = value
self.next = next_node
def hasCycle(head_node):
# This set will act as our notebook to keep a record of every single item we have visited.
visited_nodes = set()
current_node = head_node
while current_node is not None:
# Before proceeding, we check our notebook to see if we've already visited this exact item.
if current_node in visited_nodes:
# If the current item is already in our notebook, it means we have a cycle.
return True
# If we haven't seen this item before, we add it to our notebook for future reference.
visited_nodes.add(current_node)
current_node = current_node.next
return FalseThe clever solution is to imagine two pointers moving through the list, but at different speeds. By observing how these two pointers interact, we can determine if they will ever meet again, which would only happen if the path they are on is a circle.
Here's how the algorithm would work step-by-step:
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
def hasCycle(head):
# The fast runner will reach the end first if there is no cycle.
if not head or not head.next:
return False
slow_walker = head
fast_runner = head
while fast_runner and fast_runner.next:
slow_walker = slow_walker.next
fast_runner = fast_runner.next.next
# If the pointers meet, the fast runner has lapped the slow one, proving a cycle exists.
if slow_walker == fast_runner:
return True
# If the loop finishes, the fast runner reached the end, so there is no cycle.
return False| Case | How to Handle |
|---|---|
| Null head pointer (empty list) | The initial check for head being null or head.next being null correctly returns false as a cycle requires at least one node pointing to itself or another. |
| Linked list with only one node | A single node can either point to null (no cycle) or to itself (cycle), both of which are handled correctly by the two-pointer approach. |
| Linked list with two nodes | The two-pointer algorithm correctly detects both the no-cycle case (A->B->null) and the cycle case (A->B->A). |
| Cycle starts at the head of the list | The slow and fast pointers will eventually meet within the cycle, regardless of where the cycle begins. |
| A 'lollipop' structure where the cycle begins after a long non-cyclic path | The fast pointer is guaranteed to enter the cycle first and will eventually be lapped by the slow pointer within the cycle portion. |
| The entire list is one large cycle | The fast pointer will lap the slow pointer, and they will meet, correctly identifying the cycle. |
| A very long linked list (maximum size) | The two-pointer solution uses constant O(1) extra space, making it highly efficient and scalable for very large lists without memory issues. |
| A single node pointing to itself (self-loop) | The fast pointer will advance and immediately meet the slow pointer at the head, correctly identifying the cycle. |