Linked List Cycle
Problem Statement
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.
Approach: Fast and Slow Pointers (Floydâs Cycle-Finding Algorithm)
The most optimal way to detect a cycle in a linked list is by using two pointers moving at different speeds. This is known as Floydâs Cycle-Finding Algorithm (often referred to as the âTortoise and Hareâ algorithm).
- Initialize two pointers,
slowandfast, both pointing to theheadof the linked list. - The
slowpointer will move one step at a time (slow = slow.next). - The
fastpointer will move two steps at a time (fast = fast.next.next). - We loop as long as
fastandfast.nextare notnull. (If either becomesnull, weâve reached the end of the list, meaning there is no cycle). - Inside the loop, after moving both pointers:
- If
slowandfastpoint to the exact same node (slow === fast), it means thefastpointer has lapped theslowpointer. This is only physically possible if there is a cycle. We returntrue.
- If
- If the loop completes and we hit a
nullnode, we returnfalse.
Solution
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
function hasCycle(head) {
let slow = head;
let fast = head;
// As long as fast and fast.next exist, we can safely move fast by 2 steps
while (fast !== null && fast.next !== null) {
slow = slow.next; // Move slow by 1
fast = fast.next.next; // Move fast by 2
// If they meet, there is a cycle
if (slow === fast) {
return true;
}
}
// If we reach a null, there's an end to the list (no cycle)
return false;
}
Complexity Analysis
- Time Complexity: where is the number of nodes in the linked list. If there is no cycle, the fast pointer reaches the end in steps. If there is a cycle, the fast pointer will catch up to the slow pointer in at most steps.
- Space Complexity: . We only use two extra pointers (
slowandfast), so the memory footprint is constant.