Detect Cycle in Linked List Floyd's Algorithm in DSA Rust - Time & Space Complexity
We want to understand how long it takes to check if a linked list has a cycle using Floyd's Algorithm.
The question is: how does the time grow as the list gets longer?
Analyze the time complexity of the following code snippet.
fn has_cycle(head: Option<Box<Node>>) -> bool {
let mut slow = head.as_ref();
let mut fast = head.as_ref();
while let (Some(s), Some(f)) = (slow, fast) {
slow = s.next.as_ref();
fast = f.next.as_ref().and_then(|n| n.next.as_ref());
if let (Some(ref ss), Some(ref ff)) = (slow, fast) {
if std::ptr::eq(ss.as_ref(), ff.as_ref()) {
return true;
}
}
}
false
}
This code uses two pointers moving at different speeds to detect if the linked list loops back on itself.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The while loop that moves slow and fast pointers through the list.
- How many times: At most, the loop runs once per node until pointers meet or reach the end.
As the list grows, the number of steps to detect a cycle grows roughly in proportion to the number of nodes.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 steps |
| 100 | About 100 steps |
| 1000 | About 1000 steps |
Pattern observation: The steps increase linearly as the list size increases.
Time Complexity: O(n)
This means the time to detect a cycle grows in a straight line with the number of nodes in the list.
[X] Wrong: "The algorithm checks every pair of nodes, so it must be O(n²)."
[OK] Correct: The two pointers move through the list independently, not checking all pairs, so the total steps grow only linearly.
Understanding this linear time check shows you can analyze pointer-based algorithms efficiently, a key skill in many coding challenges.
"What if we used only one pointer moving one step at a time? How would the time complexity change?"
Practice
Solution
Step 1: Understand Floyd's algorithm concept
It uses two pointers, one moving faster (fast) and one slower (slow) through the list.Step 2: Detect cycle by pointer meeting
If the fast pointer meets the slow pointer, a cycle exists; otherwise, no cycle.Final Answer:
Use two pointers moving at different speeds to detect a cycle -> Option AQuick Check:
Two pointers at different speeds = cycle detection [OK]
- Thinking counting nodes detects cycle
- Assuming reversing list helps detect cycle
- Using extra memory instead of pointers
head?Solution
Step 1: Initialize both pointers at head
Floyd's algorithm starts both slow and fast pointers at the head node.Step 2: Correct Rust reference usage
Using head.as_ref() for both slow and fast is correct; fast moves faster later.Final Answer:
let mut slow = head.as_ref(); let mut fast = head.as_ref(); -> Option AQuick Check:
Both pointers start at head [OK]
- Starting fast pointer at head.next before loop
- Cloning nodes unnecessarily
- Using wrong references causing borrow errors
struct Node { val: i32, next: Option }
fn has_cycle(head: &Option) -> bool {
let mut slow = head.as_ref();
let mut fast = head.as_ref();
while fast.is_some() && fast.unwrap().next.is_some() {
slow = slow.unwrap().next.as_ref();
fast = fast.unwrap().next.as_ref().unwrap().next.as_ref();
if slow.map(|n| n as *const ()) == fast.map(|n| n as *const ()) {
return true;
}
}
false
}
// List: 1 -> 2 -> 3 -> 4 -> 2 (cycle back to node with value 2)Solution
Step 1: Understand the list structure
The list has a cycle starting at node with value 2, so cycle exists.Step 2: Trace Floyd's pointers
Slow and fast pointers will eventually meet inside the cycle, returning true.Final Answer:
true -> Option CQuick Check:
Cycle present = true [OK]
- Assuming no cycle returns true
- Confusing Option with references
- Missing unwrap() causing panic
fn has_cycle(head: &Option) -> bool {
let mut slow = head.as_ref();
let mut fast = head.as_ref();
while fast.is_some() && fast.unwrap().next.is_some() {
slow = slow.unwrap().next.as_ref();
fast = fast.unwrap().next.as_ref(); // Bug here
if slow.map(|n| n as *const ()) == fast.map(|n| n as *const ()) {
return true;
}
}
false
}Solution
Step 1: Analyze fast pointer movement
Fast pointer should move two steps each loop, but here it moves only one step.Step 2: Understand impact on cycle detection
Moving fast only one step breaks Floyd's logic, so cycle detection fails or slows.Final Answer:
fast pointer moves only one step instead of two -> Option BQuick Check:
Fast pointer must move two steps [OK]
- Moving fast pointer one step only
- Not checking fast pointer's next before moving
- Comparing pointers incorrectly
Solution
Step 1: Detect cycle using Floyd's algorithm
First, detect if cycle exists by moving slow and fast pointers.Step 2: Find cycle start node
Reset slow to head, then move slow and fast one step each until they meet; meeting point is cycle start.Final Answer:
After detecting cycle, reset slow to head and move both pointers one step until they meet -> Option DQuick Check:
Reset slow and move both pointers to find cycle start [OK]
- Trying to count nodes instead of pointer moves
- Using extra memory instead of pointer reset
- Reversing list which breaks cycle detection
Ready to prove it?
Pass the test and this pattern moves unknown
