Detect a Cycle in a Linked List
Understanding the Problem: Detecting a Cycle in a Linked List
A linked list is a linear data structure where each element (node) points to the next element in the sequence. However, in some cases, the last node might point back to one of the earlier nodes, creating a cycle (loop). The goal of this problem is to detect whether a cycle exists in the linked list.
Visualizing a Linked List without a Cycle:
Consider a normal linked list:
1 -> 2 -> 3 -> 4 -> 5 -> NULL
Here, the list terminates when the last node (with value 5
) points to NULL
, indicating the end of the list.
Visualizing a Linked List with a Cycle:
Now, consider a linked list with a cycle:
1 -> 2 -> 3 -> 4 -> 5
^ |
|___________|
In this case, the last node (with value 5
) points back to node 3
, creating a loop or cycle. This means that if you start traversing the list from node 1
, you’ll keep cycling through nodes 3
, 4
, 5
indefinitely.
Challenges of Detecting a Cycle:
Without a cycle, traversing a linked list will always end when a node points to
NULL
.With a cycle, you will never reach
NULL
. Instead, you’ll keep looping over a subset of the nodes repeatedly.
The challenge is to detect this cycle without using extra space or modifying the structure of the list. Using a brute-force approach, such as marking visited nodes, would require extra memory. Instead, we can use Floyd’s Cycle Detection Algorithm (Tortoise and Hare Algorithm), which is an efficient approach that only uses two pointers and operates in constant space.
Floyd’s Cycle Detection Algorithm (Tortoise and Hare)
The idea behind Floyd’s Cycle Detection is to use two pointers (slow
and fast
) to traverse the linked list:
Slow pointer (
slow
) moves one step at a time.Fast pointer (
fast
) moves two steps at a time.
If there is no cycle, the fast
pointer will eventually reach the end of the list (NULL
). If there is a cycle, the fast
pointer will eventually catch up to the slow
pointer because it’s moving twice as fast.
Step-by-Step Explanation with Visuals
Step 1: Initial Setup
- We start both the
slow
andfast
pointers at the head of the list.
1 -> 2 -> 3 -> 4 -> 5
^
slow, fast
Both pointers start at node 1
.
Step 2: Move the Pointers
- Move
slow
by 1 step andfast
by 2 steps:
code1 -> 2 -> 3 -> 4 -> 5
^ ^
slow fast
- Move the pointers again:
code1 -> 2 -> 3 -> 4 -> 5
^ ^
slow fast
Step 3: Detect a Cycle
- Keep moving
slow
andfast
. If there is no cycle,fast
will eventually reach the end (NULL
).
However, if there is a cycle, at some point, slow
and fast
will meet within the cycle. For example:
rustCopy code1 -> 2 -> 3 -> 4 -> 5
^ |
|___________|
- Initially:
1 -> 2 -> 3 -> 4 -> 5
^ ^
slow fast
- After several steps, they meet at node
4
:
1 -> 2 -> 3 -> 4 -> 5
^ ^
slow fast
This meeting point confirms the presence of a cycle.
Time and Space Complexity
Time Complexity:
O(n)
wheren
is the number of nodes in the list. Bothslow
andfast
traverse the list at most once.Space Complexity:
O(1)
since no additional space is used beyond the two pointers.
C++ Solution:
#include <iostream>
using namespace std;
class Node {
public:
int data;
Node* next;
Node(int val) : data(val), next(nullptr) {}
};
// Function to detect a cycle in a linked list using Floyd's Algorithm
bool hasCycle(Node* head) {
if (!head || !head->next) return false;
Node* slow = head;
Node* fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
// If slow and fast meet, a cycle exists
if (slow == fast) return true;
}
return false;
}
// Utility function to create a cycle for testing
void createCycle(Node* head, int pos) {
if (pos == -1) return;
Node* tail = head;
Node* cycleNode = nullptr;
int index = 0;
while (tail->next != nullptr) {
if (index == pos) cycleNode = tail;
tail = tail->next;
index++;
}
tail->next = cycleNode; // Creating a cycle
}
int main() {
// Create a linked list: 1 -> 2 -> 3 -> 4 -> 5 -> 6
Node* head = new Node(1);
head->next = new Node(2);
head->next->next = new Node(3);
head->next->next->next = new Node(4);
head->next->next->next->next = new Node(5);
head->next->next->next->next->next = new Node(6);
createCycle(head, 2); // Creating a cycle that connects node 6 to node 3
if (hasCycle(head)) {
cout << "Cycle detected in the list." << endl;
} else {
cout << "No cycle detected in the list." << endl;
}
return 0;
}
Java Solution:
class ListNode {
int val;
ListNode next;
ListNode(int x) { val = x; next = null; }
}
public class LinkedListCycle {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) return false;
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
// If slow and fast meet, a cycle exists
if (slow == fast) return true;
}
return false; // No cycle
}
// Utility function to create a cycle for testing
public void createCycle(ListNode head, int pos) {
if (pos == -1) return;
ListNode tail = head;
ListNode cycleNode = null;
int index = 0;
while (tail.next != null) {
if (index == pos) cycleNode = tail;
tail = tail.next;
index++;
}
tail.next = cycleNode; // Creating a cycle
}
public static void main(String[] args) {
LinkedListCycle solution = new LinkedListCycle();
// Create linked list: 1 -> 2 -> 3 -> 4 -> 5 -> 6
ListNode head = new ListNode(1);
head.next = new ListNode(2);
head.next.next = new ListNode(3);
head.next.next.next = new ListNode(4);
head.next.next.next.next = new ListNode(5);
head.next.next.next.next.next = new ListNode(6);
solution.createCycle(head, 2); // Create a cycle at node 3
if (solution.hasCycle(head)) {
System.out.println("Cycle detected in the list.");
} else {
System.out.println("No cycle detected in the list.");
}
}
}
Python Solution:
class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def hasCycle(head):
if not head or not head.next:
return False
slow = head
fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
# If slow and fast meet, there is a cycle
if slow == fast:
return True
return False # No cycle
# Utility function to create a cycle for testing
def createCycle(head, pos):
if pos == -1:
return
tail = head
cycleNode = None
index = 0
while tail.next:
if index == pos:
cycleNode = tail
tail = tail.next
index += 1
tail.next = cycleNode # Creating a cycle
# Example usage:
head = ListNode(1)
head.next = ListNode(2)
head.next.next = ListNode(3)
head.next.next.next = ListNode(4)
head.next.next.next.next = ListNode(5)
head.next.next.next.next.next = ListNode(6)
createCycle(head, 2) # Create a cycle connecting node 6 to node 3
if hasCycle(head):
print("Cycle detected in the list.")
else:
print("No cycle detected in the list.")