Linked List Cycle Detection #
Problem Statement #
Given the head of a linked list, determine if the linked list has a cycle in it.
A cycle exists in a linked list if there is some node in the list that can be reached again by continuously following the next pointer. The cycle can occur anywhere in the list, not necessarily at the end.
Return true if there is a cycle in the linked list, otherwise return false.
Challenge: Solve this without using any additional data structures (like arrays or hash tables).
Method Signature #
public static boolean hasCycle(ListNode head)
ListNode Definition #
class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
Examples #
Example 1: Cycle exists
Input: head = [3,2,0,-4], cycle connects tail to node index 1
Output: true
Visual:
3 -> 2 -> 0 -> -4
↑ |
+---------+
Explanation: There is a cycle where the tail connects back to the 2nd node (0-indexed).
Example 2: Cycle exists (simple)
Input: head = [1,2], cycle connects tail to node index 0
Output: true
Visual:
1 -> 2
↑ |
+----+
Explanation: There is a cycle where the tail connects back to the first node.
Example 3: No cycle
Input: head = [1,2,3,4,5]
Output: false
Visual:
1 -> 2 -> 3 -> 4 -> 5 -> null
Explanation: The list ends with null, so there is no cycle.
Example 4: Single node, no cycle
Input: head = [1]
Output: false
Visual:
1 -> null
Explanation: Single node pointing to null has no cycle.
Example 5: Single node with cycle
Input: head = [1], cycle connects to itself
Output: true
Visual:
1 --+
↑ |
+---+
Explanation: The single node points to itself, creating a cycle.
Test Your Solution #
public class LinkedListCycleTest {
public static void main(String[] args) {
// Test case 1: No cycle [1,2,3,4,5]
ListNode test1 = new ListNode(1);
test1.next = new ListNode(2);
test1.next.next = new ListNode(3);
test1.next.next.next = new ListNode(4);
test1.next.next.next.next = new ListNode(5);
System.out.println("Test 1 (no cycle): " + hasCycle(test1)); // Expected: false
// Test case 2: Cycle [3,2,0,-4] with tail -> index 1
ListNode test2 = new ListNode(3);
test2.next = new ListNode(2);
test2.next.next = new ListNode(0);
test2.next.next.next = new ListNode(-4);
test2.next.next.next.next = test2.next; // Create cycle: -4 -> 2
System.out.println("Test 2 (has cycle): " + hasCycle(test2)); // Expected: true
// Test case 3: Simple cycle [1,2] with tail -> index 0
ListNode test3 = new ListNode(1);
test3.next = new ListNode(2);
test3.next.next = test3; // Create cycle: 2 -> 1
System.out.println("Test 3 (simple cycle): " + hasCycle(test3)); // Expected: true
// Test case 4: Single node, no cycle
ListNode test4 = new ListNode(1);
System.out.println("Test 4 (single, no cycle): " + hasCycle(test4)); // Expected: false
// Test case 5: Single node with self cycle
ListNode test5 = new ListNode(1);
test5.next = test5; // Points to itself
System.out.println("Test 5 (self cycle): " + hasCycle(test5)); // Expected: true
// Test case 6: Empty list
System.out.println("Test 6 (empty): " + hasCycle(null)); // Expected: false
}
// Your implementation goes here
public static boolean hasCycle(ListNode head) {
// TODO: Implement cycle detection
return false;
}
}
Solution
Extension Challenges #
- Find cycle start: Return the node where the cycle begins
- Cycle length: Find the length of the cycle if one exists
- Multiple cycles: Detect if there are multiple separate cycles
- Remove cycle: Break the cycle by setting appropriate next pointer to null
- Doubly linked list: Adapt algorithm for doubly linked lists
Real-World Applications #
- Memory leak detection: Detect circular references in object graphs
- Infinite loop prevention: Detect cycles in state machines or workflows
- Graph algorithms: Detect cycles in linked graph representations
- Resource management: Prevent circular dependencies in resource allocation
- Data validation: Ensure data structures don't have unintended cycles