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 or ensuring data structures' integrity.
Potential pitfalls include misunderstanding the problem's constraints or failing to handle edge cases, such as an empty list or a single node without a cycle.
To solve this problem, we can use two primary approaches:
In the naive approach, we use a hash set to keep track of visited nodes. As we traverse the linked list, we check if the current node is already in the set. If it is, we return true, indicating a cycle. If we reach the end of the list without finding a cycle, we return false.
In this approach, we use two pointers, slow and fast. 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.
// Naive approach using a hash set
var hasCycle = function(head) {
let visited = new Set();
let current = head;
while (current !== null) {
// If the current node is already in the set, we have a cycle
if (visited.has(current)) {
return true;
}
// Add the current node to the set
visited.add(current);
// Move to the next node
current = current.next;
}
// If we reach here, there is no cycle
return false;
};
// Optimized approach using Floyd's Tortoise and Hare algorithm
var hasCycle = function(head) {
if (head === null || head.next === null) {
return false;
}
let slow = head;
let fast = head.next;
while (slow !== fast) {
if (fast === null || fast.next === null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
};
Naive Approach:
Optimized Approach:
Consider the following edge cases:
To test the solution comprehensively, consider the following test cases:
// Test case 1: List with a cycle
let head1 = { val: 3, next: null };
let node2 = { val: 2, next: null };
let node3 = { val: 0, next: null };
let node4 = { val: -4, next: null };
head1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node2; // Creates a cycle
console.log(hasCycle(head1)); // Output: true
// Test case 2: List without a cycle
let head2 = { val: 1, next: null };
let node5 = { val: 2, next: null };
head2.next = node5;
console.log(hasCycle(head2)); // Output: false
// Test case 3: Empty list
console.log(hasCycle(null)); // Output: false
// Test case 4: Single node without a cycle
let head3 = { val: 1, next: null };
console.log(hasCycle(head3)); // Output: false
// Test case 5: Single node with a cycle
let head4 = { val: 1, next: null };
head4.next = head4; // Creates a cycle
console.log(hasCycle(head4)); // Output: true
When approaching such problems, consider the following tips:
Detecting a cycle in a linked list is a common problem with practical applications. Understanding and implementing both the naive and optimized approaches helps in grasping the underlying concepts. Practice and familiarity with such problems will improve your problem-solving skills.
For further reading and practice, consider the following resources: