Approach Summary
Floyd's tortoise and hare: slow moves 1, fast moves 2. If they meet, there is a cycle.
Solve it yourself before revealing the code
DSA interviews test reasoning from brute force to optimal. Think through the constraints first.
Solution Progression (Brute Force → Most Optimized)
Understand the architectural evolution from naive baseline to optimal complexity.
| Approach | Time | Space | Core Strategy / Trade-off |
|---|---|---|---|
| 1 Approach 1: Hash Set Visited Tracking | O(n) | O(n) | Record visited nodes in a set; return true if a node is encountered a second time. |
| 2 Approach 2: Floyd's Tortoise and Hare (Optimal) | O(n) | O(1) | Fast pointer moves 2 steps, slow moves 1 step; if they meet, a cycle exists. |
Approach 1: Hash Set Visited Tracking
Maintain a hash set of node pointers. Traverse the list node by node. If the current node is already in the set, a cycle has been detected. Otherwise, insert it and continue until reaching null.
Approach 1: Hash Set Visited Tracking Implementation
Approach 1: Hash Set Visited Tracking
public class Solution {
public boolean hasCycle(ListNode head) {
Set<ListNode> visited = new HashSet<>();
ListNode curr = head;
while (curr != null) {
if (visited.contains(curr)) return true;
visited.add(curr);
curr = curr.next;
}
return false;
}
}class Solution {
public:
bool hasCycle(ListNode *head) {
unordered_set<ListNode*> visited;
ListNode* curr = head;
while (curr != NULL) {
if (visited.find(curr) != visited.end()) return true;
visited.insert(curr);
curr = curr->next;
}
return false;
}
};class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
visited = set()
curr = head
while curr:
if curr in visited:
return True
visited.add(curr)
curr = curr.next
return Falsefunction hasCycle(head) {
const visited = new Set();
let curr = head;
while (curr !== null) {
if (visited.has(curr)) return true;
visited.add(curr);
curr = curr.next;
}
return false;
} Approach 2: Floyd's Tortoise and Hare (Optimal)
By moving fast at twice the speed of slow, each step reduces the distance between fast and slow by 1 inside any cycle. Because the distance decreases by 1 modulo cycle length on each step, fast must collide with slow without ever jumping over it.
Approach 2: Floyd's Tortoise and Hare (Optimal) Implementation
Approach 2: Floyd's Tortoise and Hare (Optimal)
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null) return false;
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;
}
}class Solution {
public:
bool hasCycle(ListNode *head) {
if (!head) return false;
ListNode* slow = head;
ListNode* fast = head;
while (fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) return true;
}
return false;
}
};class Solution:
def hasCycle(self, head: Optional[ListNode]) -> bool:
slow = fast = head
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast:
return True
return Falsefunction hasCycle(head) {
if (!head) return false;
let slow = head, fast = head;
while (fast && fast.next) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) return true;
}
return false;
} Edge Cases to Watch
- Empty list (head is null; returns false)
- Single node with no cycle (node.next is null; returns false)
- Single node pointing to itself (node.next is node; returns true)
- Two nodes forming a cycle (1 -> 2 -> 1; returns true)
Master the Linked List Pattern
Study the core invariant, step-by-step decision framework, and common interview traps for the Linked List pattern.
How to Recognize This Pattern
- Detect cycle in linked list
- O(1) space required
Complexity Analysis
Time Complexity
O(n)
Space Complexity
O(1)