algos/cs/graph/
floyd_cycle.rs

1//! # Floyd's Cycle Detection (Tortoise and Hare)
2//!
3//! This module provides a standard implementation of Floyd's
4//! Cycle Detection (a.k.a. Tortoise and Hare) for singly-linked lists. This
5//! algorithm detects whether a cycle exists in \( O(n) \) time and \( O(1) \)
6//! extra space, and can also identify the node where the cycle begins.
7//!
8//! ## Overview
9//!
10//! Floyd's Cycle Detection uses two pointers (slow and fast). Slow advances by
11//! one node at a time, while fast advances by two nodes at a time. If they ever
12//! point to the same node, a cycle exists. To find the *start* of the cycle,
13//! reset one pointer to the head and advance both by one node at a time. The
14//! node where they meet is the start of the cycle.
15//!
16//! ## Example Usage
17//!
18//! ```rust
19//! use std::rc::Rc;
20//! use std::cell::RefCell;
21//!
22//! // Suppose we have the following list: 1 -> 2 -> 3 -> 4 -> 5
23//! // We'll create it and introduce a cycle from 5 back to node 3.
24//! use algos::cs::graph::floyd_cycle::{ListNode, has_cycle, find_cycle_start};
25//!
26//! // Create each node (wrapped in Rc<RefCell<>> to allow shared ownership)
27//! let node1 = Rc::new(RefCell::new(ListNode::new(1)));
28//! let node2 = Rc::new(RefCell::new(ListNode::new(2)));
29//! let node3 = Rc::new(RefCell::new(ListNode::new(3)));
30//! let node4 = Rc::new(RefCell::new(ListNode::new(4)));
31//! let node5 = Rc::new(RefCell::new(ListNode::new(5)));
32//!
33//! // Link them: 1->2->3->4->5
34//! node1.borrow_mut().next = Some(node2.clone());
35//! node2.borrow_mut().next = Some(node3.clone());
36//! node3.borrow_mut().next = Some(node4.clone());
37//! node4.borrow_mut().next = Some(node5.clone());
38//!
39//! // Introduce a cycle: 5 -> 3
40//! node5.borrow_mut().next = Some(node3.clone());
41//!
42//! let head = Some(node1.clone());
43//!
44//! assert_eq!(has_cycle(&head), true);
45//! let start_node = find_cycle_start(&head).unwrap();
46//! assert_eq!(start_node.borrow().val, 3); // The cycle starts at node with value 3
47//! ```
48
49use std::cell::RefCell;
50use std::rc::Rc;
51
52/// A singly-linked list node that can share ownership (via `Rc`) and be
53/// modified (via `RefCell`). This allows creating cycles for testing or
54/// demonstration of cycle detection.
55#[derive(Debug)]
56pub struct ListNode<T> {
57    pub val: T,
58    pub next: Option<Rc<RefCell<ListNode<T>>>>,
59}
60
61impl<T> ListNode<T> {
62    /// Creates a new `ListNode` with the given value and no next pointer.
63    pub fn new(val: T) -> Self {
64        ListNode { val, next: None }
65    }
66}
67
68/// Determines if a singly-linked list has a cycle using Floyd's Tortoise and Hare.
69/// - `head`: The head node of the list (or `None` if empty).
70/// - Returns `true` if there's a cycle, `false` otherwise.
71pub fn has_cycle<T>(head: &Option<Rc<RefCell<ListNode<T>>>>) -> bool {
72    // If list is empty or has no next, no cycle
73    let mut slow = head.clone();
74    let mut fast = head.clone();
75
76    while let Some(f) = fast {
77        // Advance fast pointer by one
78        let next_fast = f.borrow().next.clone();
79        if let Some(f2) = next_fast {
80            // Advance fast pointer by second step
81            fast = f2.borrow().next.clone();
82        } else {
83            // Next step not available => no cycle
84            return false;
85        }
86
87        // Advance slow pointer by one
88        if let Some(s) = slow.clone() {
89            slow = s.borrow().next.clone();
90        }
91
92        // If they meet, cycle detected
93        if let (Some(sf), Some(ff)) = (slow.clone(), fast.clone()) {
94            if Rc::ptr_eq(&sf, &ff) {
95                return true;
96            }
97        } else {
98            return false;
99        }
100    }
101    false
102}
103
104/// If a cycle exists, returns the node (as `Rc<RefCell<ListNode<T>>>`) where
105/// the cycle begins. If no cycle exists, returns `None`.
106///
107/// The algorithm first uses Floyd's Tortoise and Hare to detect a meeting point.
108/// If no meeting point exists, there's no cycle. If it does exist, we reset one
109/// pointer to head and advance both one step at a time until they meet again.
110/// That node is the start of the cycle.
111pub fn find_cycle_start<T>(
112    head: &Option<Rc<RefCell<ListNode<T>>>>,
113) -> Option<Rc<RefCell<ListNode<T>>>> {
114    // Early exit for empty list
115    if head.is_none() {
116        return None;
117    }
118    let mut slow = head.clone();
119    let mut fast = head.clone();
120    let mut intersection: Option<Rc<RefCell<ListNode<T>>>> = None;
121
122    // Phase 1: Detect cycle
123    while let Some(f) = fast {
124        let next_fast = f.borrow().next.clone();
125        if let Some(f2) = next_fast {
126            fast = f2.borrow().next.clone();
127        } else {
128            return None; // No cycle if we can't advance fast pointer
129        }
130
131        if let Some(s) = slow.clone() {
132            slow = s.borrow().next.clone();
133        }
134
135        if let (Some(sf), Some(ff)) = (slow.clone(), fast.clone()) {
136            if Rc::ptr_eq(&sf, &ff) {
137                intersection = Some(sf);
138                break;
139            }
140        } else {
141            return None;
142        }
143    }
144
145    // If no intersection was found, no cycle
146    intersection.as_ref()?;
147
148    // Phase 2: Find start of cycle
149    let mut ptr1 = head.clone();
150    let mut ptr2 = intersection;
151    while let (Some(p1), Some(p2)) = (ptr1, ptr2) {
152        if Rc::ptr_eq(&p1, &p2) {
153            return Some(p1);
154        }
155        ptr1 = p1.borrow().next.clone();
156        ptr2 = p2.borrow().next.clone();
157    }
158    None
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn test_empty_list() {
167        assert_eq!(has_cycle::<i32>(&None), false);
168        assert!(find_cycle_start::<i32>(&None).is_none());
169    }
170
171    #[test]
172    fn test_single_node_no_cycle() {
173        let n1 = Rc::new(RefCell::new(ListNode::new(42)));
174        let head = Some(n1.clone());
175        assert_eq!(has_cycle(&head), false);
176        assert!(find_cycle_start(&head).is_none());
177    }
178
179    #[test]
180    fn test_two_nodes_no_cycle() {
181        let n1 = Rc::new(RefCell::new(ListNode::new(1)));
182        let n2 = Rc::new(RefCell::new(ListNode::new(2)));
183        n1.borrow_mut().next = Some(n2.clone());
184
185        let head = Some(n1.clone());
186        assert_eq!(has_cycle(&head), false);
187        assert!(find_cycle_start(&head).is_none());
188    }
189
190    #[test]
191    fn test_small_cycle() {
192        let n1 = Rc::new(RefCell::new(ListNode::new(1)));
193        let n2 = Rc::new(RefCell::new(ListNode::new(2)));
194        let n3 = Rc::new(RefCell::new(ListNode::new(3)));
195
196        // 1 -> 2 -> 3 -> back to 2
197        n1.borrow_mut().next = Some(n2.clone());
198        n2.borrow_mut().next = Some(n3.clone());
199        n3.borrow_mut().next = Some(n2.clone());
200
201        let head = Some(n1.clone());
202        assert_eq!(has_cycle(&head), true);
203
204        let start = find_cycle_start(&head).unwrap();
205        assert!(Rc::ptr_eq(&start, &n2));
206    }
207
208    #[test]
209    fn test_longer_cycle() {
210        // 1 -> 2 -> 3 -> 4 -> 5
211        //              ^---------|
212        let n1 = Rc::new(RefCell::new(ListNode::new(1)));
213        let n2 = Rc::new(RefCell::new(ListNode::new(2)));
214        let n3 = Rc::new(RefCell::new(ListNode::new(3)));
215        let n4 = Rc::new(RefCell::new(ListNode::new(4)));
216        let n5 = Rc::new(RefCell::new(ListNode::new(5)));
217
218        n1.borrow_mut().next = Some(n2.clone());
219        n2.borrow_mut().next = Some(n3.clone());
220        n3.borrow_mut().next = Some(n4.clone());
221        n4.borrow_mut().next = Some(n5.clone());
222
223        // create cycle
224        n5.borrow_mut().next = Some(n3.clone());
225
226        let head = Some(n1.clone());
227
228        assert_eq!(has_cycle(&head), true);
229        let start = find_cycle_start(&head).unwrap();
230        assert!(Rc::ptr_eq(&start, &n3));
231    }
232
233    #[test]
234    fn test_no_cycle_long_list() {
235        // 1 -> 2 -> 3 -> 4 -> 5 -> None
236        let n1 = Rc::new(RefCell::new(ListNode::new(1)));
237        let n2 = Rc::new(RefCell::new(ListNode::new(2)));
238        let n3 = Rc::new(RefCell::new(ListNode::new(3)));
239        let n4 = Rc::new(RefCell::new(ListNode::new(4)));
240        let n5 = Rc::new(RefCell::new(ListNode::new(5)));
241
242        n1.borrow_mut().next = Some(n2.clone());
243        n2.borrow_mut().next = Some(n3.clone());
244        n3.borrow_mut().next = Some(n4.clone());
245        n4.borrow_mut().next = Some(n5.clone());
246
247        let head = Some(n1.clone());
248        assert_eq!(has_cycle(&head), false);
249        assert!(find_cycle_start(&head).is_none());
250    }
251}