Introduction & Problem Explanation
The Detect Cycle in a Linked List problem asks us to determine if a given linked list
contains a cycle. A cycle exists if there is some node in the list that can be reached again by continuously
following the next pointer.
For example, if the list is 3 -> 2 -> 0 -> -4 where -4 points back to
2, a cycle exists. If it terminates at null, there is no cycle. Reaching the end of
a list without infinite loops is critical for memory safety and pointer traversal.
Imagine two runners—a Tortoise and a Hare—on a race track. The Tortoise runs slowly, taking 1 step at a time. The Hare runs fast, taking 2 steps at a time. If the track is a straight path with a dead end, the Hare will quickly reach the end and wait. However, if the track forms a loop, the Hare will go round and round. Eventually, because the Hare is faster, they will enter the loop and lap the Tortoise, meeting at the exact same location! By checking if they ever meet, we can prove if a loop exists.
The Algorithmic Approach
1. HashSet Visited Tracker (O(n) Time, O(n) Space)
We can traverse the linked list and store the reference of each visited node in a HashSet. If we encounter a
node that is already present in the set, we know there is a cycle. While simple, this requires
O(n) extra memory to store node references.
2. Floyd's Cycle Detection Algorithm (O(n) Time, O(1) Space)
Also known as the **Tortoise and Hare** algorithm, this approach uses two pointers moving at different speeds:
slow: Moves 1 node at a time (slow = slow.next).fast: Moves 2 nodes at a time (fast = fast.next.next).
fast pointer will reach the end of the list (null). If there
is a cycle, the fast pointer will enter the cycle and catch up to the slow pointer.
When slow == fast, a cycle is detected. This is optimal because it uses constant memory.
Step-by-Step Execution Walkthrough
Let's trace the algorithm on the list 3 -> 2 -> 0 -> -4 where -4 points back to
2:
- Step 1 (Initialization): Both
slowandfastpointers are set to theheadnode (node3).slow = 3,fast = 3
- Step 2 (Iteration 1):
slowmoves 1 step to node2.fastmoves 2 steps to node0.- Compare pointers:
slow (2) != fast (0). We continue.
- Step 3 (Iteration 2):
slowmoves 1 step to node0.fastmoves 2 steps (from0to-4and then back to2).fast = 2.- Compare pointers:
slow (0) != fast (2). We continue.
- Step 4 (Iteration 3):
slowmoves 1 step to node-4.fastmoves 2 steps (from2to0and then to-4).fast = -4.- Compare pointers:
slow (-4) == fast (-4). The pointers have met!
- Step 5 (Completion): Since
slow == fast, the loop ends and the method immediately returnstrue, confirming the presence of a cycle.
Key Code Snippets & Explanations
Here is why the main logic in the solution is important:
while (fast != null && fast.next != null): Ensures we do not encounter a NullPointerException when checking the fast runner's future steps. If either is null, we reached the end of a straight list, meaning no cycle exists.slow = slow.next; fast = fast.next.next;: Advances the pointers at different speeds, allowing the Hare to lap the Tortoise mathematically within a cycle.if (slow == fast) return true;: The definitive meeting condition proving the cycle's existence.
Java Implementation Code
Below is the complete, self-contained Java source code that solves this problem. It also includes a
main method that traces the execution with console outputs.
package io.practise.dsa;
public class DetectCycleLinkedList {
public static class ListNode {
public int val;
public ListNode next;
public ListNode(int val) { this.val = val; }
}
// Optimized - Floyd's Cycle Detection - O(n)
public boolean hasCycle(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) return true;
}
return false;
}
public static void main(String[] args) {
DetectCycleLinkedList solver = new DetectCycleLinkedList();
ListNode head = new ListNode(3);
ListNode cycleNode = new ListNode(2);
head.next = cycleNode;
head.next.next = new ListNode(0);
head.next.next.next = new ListNode(-4);
head.next.next.next.next = cycleNode; // Cycle created
System.out.println("--- Detect Cycle in Linked List Demonstration ---");
System.out.println("Floyd's Tortoise and Hare algorithm maps fast pointer and slow pointer.");
System.out.println("Has Cycle: " + solver.hasCycle(head));
}
}
Conclusion
Solving the Detect Cycle in Linked List problem highlights how we can leverage key data structures and algorithmic paradigms (like two pointers, hash maps, or dynamic programming) to significantly optimize runtime and simplify code complexity.