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?
The core challenge of this problem is to detect if a linked list contains a cycle. A cycle occurs when a node's next pointer points back to a previous node, creating a loop. This problem is significant in various applications such as detecting infinite loops in programs, network routing, and more.
Potential pitfalls include misunderstanding the cycle detection mechanism and not handling edge cases like an empty list or a single node without a cycle.
To solve this problem, we can use two main approaches:
In the naive approach, we traverse the linked list and store each visited node in a hash set. If we encounter a node that is already in the set, we return true, indicating a cycle. Otherwise, we return false if we reach the end of the list.
In this approach, we use two pointers, slow (tortoise) and fast (hare). 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 meet the slow pointer. If the fast pointer reaches the end of the list, there is no cycle.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
def hasCycle(head):
visited = set()
current = head
while current:
if current in visited:
return True
visited.add(current)
current = current.next
return False
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
def hasCycle(head):
if not head or not head.next:
return False
slow = head
fast = head.next
while slow != fast:
if not fast or not fast.next:
return False
slow = slow.next
fast = fast.next.next
return True
Naive Approach:
Floyd's Tortoise and Hare Algorithm:
Consider the following edge cases:
To test the solution comprehensively, consider the following test cases:
def test_hasCycle():
# Test case 1: Empty list
assert hasCycle(None) == False
# Test case 2: Single node without cycle
node1 = ListNode(1)
assert hasCycle(node1) == False
# Test case 3: Single node with cycle
node1.next = node1
assert hasCycle(node1) == True
# Test case 4: Multiple nodes without cycle
node1 = ListNode(1)
node2 = ListNode(2)
node1.next = node2
assert hasCycle(node1) == False
# Test case 5: Multiple nodes with cycle
node3 = ListNode(3)
node4 = ListNode(4)
node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node2
assert hasCycle(node1) == True
test_hasCycle()
When approaching such problems, consider the following tips:
Detecting a cycle in a linked list is a common problem with applications in various fields. Understanding different approaches and their trade-offs is crucial for solving such problems efficiently. Practice and exploration of similar problems can help improve problem-solving skills.
For further reading and practice, consider the following resources: