dsu_tree/
lib.rs

1//! This crate provides a non-invasive implementation of a **disjoint-set tree** data structure.
2//!
3//! # Disjoint-Set Tree
4//!
5//! **[Disjoint sets] data structure**, or **DSU**, or **union-find data structure**, or **merge-
6//! find set**, is a data structure that stores a collection of disjoint sets. It provides
7//! operations for adding new sets, merging sets (equivalent to replacing the sets with the union
8//! of them) and finding the representative member of a set. DSU is very useful in implementing
9//! algorithms like [minimum spanning tree] and more.
10//!
11//! DSU can be implemented with its extreme efficiency by using **disjoint-set trees**. Disjoint-set
12//! trees are actually a forest in which each node represents a set and each tree represents the
13//! union of sets that are merged together. The three DSU operations can be implemented as follows:
14//! - Adding new sets: Easy. Just add new nodes to the forest and it's done. The new nodes are
15//!   themselves a tree in the forest to indicate that they have not been merged with other sets.
16//! - Merging sets: To merge two sets whose corresponding nodes are `A` and `B`, respectively, we
17//!   just change the parent node of `A` to `B` and it's done. In real implementations, some corner
18//!   cases need to be considered, such as merging a set into itself.
19//! - Finding the representative member of a set: Each tree within the disjoint-set trees represents
20//!   a set. The representative member of a set can be chosen to be the representative member of the
21//!   set corresponding to the root node of the tree.
22//!
23//! # `dsu-tree`
24//!
25//! Rather than implementing a disjoint-set data structure, this crate provides the implementation
26//! of the underlying disjoint-set tree data structure.
27//!
28//! [`DsuRoot`] is provided to access the disjoint-set trees. It is a smart pointer that can be
29//! thought as "always" points to the root node of a tree within the disjoint-set trees.
30//!
31//! To create a new disjoint-set tree node, call the [`DsuRoot::new`] function. To merge two
32//! disjoint-set trees, call the [`DsuRoot::merge_into`] function. To test whether two disjoint-set
33//! trees are actually the same tree, call the [`DsuRoot::same`] function.
34//!
35//! # `#![no_std]`
36//!
37//! This crate is `#![no_std]`.
38//!
39//! [Disjoint sets]: https://en.wikipedia.org/wiki/Disjoint-set_data_structure
40//! [minimum spanning tree]: https://en.wikipedia.org/wiki/Minimum_spanning_tree
41//!
42//! [`DsuRoot`]: struct.DsuRoot.html
43//! [`DsuRoot::new`]: struct.DsuRoot.html#method.new
44//! [`DsuRoot::merge_into`]: struct.DsuRoot.html#method.merge_into
45//! [`DsuRoot::same`]: struct.DsuRoot.html#method.same
46//!
47
48#![no_std]
49
50extern crate alloc;
51
52use alloc::rc::Rc;
53use core::cell::RefCell;
54
55/// An owning smart pointer that always points to the root of a DSU tree.
56///
57/// One can logically consider that `DsuRoot` smart pointers refer to a standalone DSU tree. When
58/// merging two DSU trees by calling the [`merge_into`] function, the two DSU trees are given
59/// through two `DsuRoot` smart pointers that logically refer to the two trees.
60///
61/// [`merge_into`]: #method.merge_into
62#[derive(Clone, Debug)]
63pub struct DsuRoot<T> {
64    node: Rc<DsuNode<T>>,
65}
66
67impl<T> DsuRoot<T> {
68    /// Create a new DSU tree node and attach a `DsuRoot` smart pointer to the new node.
69    ///
70    /// The new DSU tree node contains the given value.
71    pub fn new(value: T) -> Self {
72        Self {
73            node: Rc::new(DsuNode::new(value)),
74        }
75    }
76
77    /// Determine whether the two `DsuRoot` smart pointers refer to the same tree.
78    ///
79    /// ```
80    /// use dsu_tree::DsuRoot;
81    ///
82    /// let mut dsu_1 = DsuRoot::new(10);
83    /// let mut dsu_2 = DsuRoot::new(20);
84    /// assert!(!DsuRoot::same(&mut dsu_1, &mut dsu_2));
85    ///
86    /// dsu_1.merge_into(&mut dsu_2);
87    /// assert!(DsuRoot::same(&mut dsu_1, &mut dsu_2));
88    /// ```
89    pub fn same(lhs: &mut Self, rhs: &mut Self) -> bool {
90        lhs.move_to_root();
91        rhs.move_to_root();
92        Rc::ptr_eq(&lhs.node, &rhs.node)
93    }
94
95    /// Get the value contained in the DSU tree node pointed to by this `DsuRoot` smart pointer.
96    ///
97    /// This function requires `&mut self` since the `DsuRoot` smart pointer may move around over
98    /// the DSU tree so that it eventually points to the root node.
99    pub fn value(&mut self) -> &T {
100        self.move_to_root();
101        &self.node.value
102    }
103
104    /// Merge two DSU trees into one.
105    ///
106    /// The first DSU tree is given through `self`. The second DSU tree is given through `another`.
107    ///
108    /// If initially, the two trees are the same tree, then this function does nothing. Otherwise,
109    /// the parent node of the root node of the tree specified through `self` is set to the root
110    /// node of the tree specified through `another`.
111    pub fn merge_into(&mut self, another: &mut Self) {
112        if Self::same(self, another) {
113            return;
114        }
115
116        // After calling `same`, `self` and `another` should both point to the root of their DSU
117        // tree.
118        debug_assert!(self.node.is_root());
119        debug_assert!(another.node.is_root());
120
121        self.node.set_parent(another.node.clone())
122    }
123
124    /// Move this smart pointer to make it point to the root node of the referring DSU tree.
125    fn move_to_root(&mut self) {
126        self.node.compress_path();
127        if !self.node.is_root() {
128            self.node = self.node.parent().unwrap();
129        }
130
131        debug_assert!(self.node.is_root());
132    }
133}
134
135/// The DSU tree node.
136#[derive(Debug)]
137struct DsuNode<T> {
138    parent: RefCell<Option<Rc<DsuNode<T>>>>,
139    value: T,
140}
141
142impl<T> DsuNode<T> {
143    /// Create a new DSU tree node that contains the given value.
144    fn new(value: T) -> Self {
145        Self {
146            parent: RefCell::new(None),
147            value,
148        }
149    }
150
151    /// Determine whether this node is the root node.
152    fn is_root(&self) -> bool {
153        self.parent.borrow().is_none()
154    }
155
156    /// Get the parent node wrapped in an [`Option`].
157    ///
158    /// If this node is the root node, this function returns [`None`].
159    ///
160    /// [`Option`]: https://doc.rust-lang.org/core/option/enum.Option.html
161    /// [`None`]: https://doc.rust-lang.org/core/option/enum.Option.html#variant.None
162    fn parent(&self) -> Option<Rc<DsuNode<T>>> {
163        (*self.parent.borrow()).clone()
164    }
165
166    /// Set the parent node.
167    //noinspection ALL
168    fn set_parent(&self, parent: Rc<DsuNode<T>>) {
169        *self.parent.borrow_mut() = Some(parent);
170    }
171
172    /// Compress the path from this node to the root node of the tree.
173    ///
174    /// After path compression, the number of nodes along the path from this node to the root node
175    /// cannot be greater than 2. That is, after path compression, one of the two conditions holds:
176    /// * This node is initially the root node, and so it keeps to be the root node after path
177    ///   compression;
178    /// * This node is not the root node initially, and after path compression, the parent node
179    ///   becomes the root node.
180    fn compress_path(&self) {
181        // Quick check to bail the trivial situations out in which:
182        // * `self` is itself a root node;
183        // * The parent node of `self` is a root node.
184        //
185        // In any of the two cases above, we don't have to do anything.
186        let trivial = {
187            let parent_borrow = self.parent.borrow();
188            match &*parent_borrow {
189                Some(parent) => parent.is_root(),
190                None => true,
191            }
192        };
193        if trivial {
194            return;
195        }
196
197        // Do the path compression.
198        let mut parent_borrow = self.parent.borrow_mut();
199
200        // First, compress the path from the parent to the root. After this step, the parent of the
201        // parent node should be the root.
202        let parent_node = parent_borrow.as_ref().unwrap();
203        parent_node.compress_path();
204
205        // Then, simply update the parent pointer.
206        *parent_borrow = Some(parent_node.parent().unwrap());
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213    use alloc::vec::Vec;
214
215    fn create_link_tree() -> Vec<Rc<DsuNode<i32>>> {
216        let mut nodes = Vec::with_capacity(10);
217
218        let root = Rc::new(DsuNode::new(0));
219        nodes.push(root.clone());
220
221        for i in 1..10usize {
222            let mut node = DsuNode::new(i as i32);
223            node.parent = RefCell::new(Some(nodes[i - 1].clone()));
224
225            nodes.push(Rc::new(node));
226        }
227
228        nodes
229    }
230
231    fn assert_path_compressed(nodes: &[Rc<DsuNode<i32>>]) {
232        let root = nodes[0].clone();
233        for i in 1..nodes.len() {
234            let parent = nodes[i].parent();
235            assert!(parent.is_some());
236            assert!(Rc::ptr_eq(parent.as_ref().unwrap(), &root));
237        }
238    }
239
240    mod dsu_node_tests {
241        use super::*;
242
243        #[test]
244        fn test_new() {
245            let node = DsuNode::new(10);
246
247            assert!(node.parent.borrow().is_none());
248            assert_eq!(node.value, 10);
249        }
250
251        #[test]
252        fn test_is_root_true() {
253            let node = DsuNode::new(10);
254            assert!(node.is_root());
255        }
256
257        #[test]
258        fn test_is_root_false() {
259            let mut node = DsuNode::new(10);
260            node.parent = RefCell::new(Some(Rc::new(DsuNode::new(20))));
261
262            assert!(!node.is_root());
263        }
264
265        #[test]
266        fn test_parent_root() {
267            let node = DsuNode::new(10);
268
269            assert!(node.parent().is_none());
270        }
271
272        #[test]
273        fn test_parent_non_root() {
274            let mut node = DsuNode::new(10);
275            let root = Rc::new(DsuNode::new(20));
276            node.parent = RefCell::new(Some(root.clone()));
277
278            let node_parent = node.parent();
279            assert!(node_parent.is_some());
280            assert!(Rc::ptr_eq(node_parent.as_ref().unwrap(), &root));
281        }
282
283        #[test]
284        fn test_set_parent() {
285            let node = DsuNode::new(10);
286            let parent = Rc::new(DsuNode::new(20));
287
288            node.set_parent(parent.clone());
289
290            assert!(node.parent.borrow().is_some());
291            assert!(Rc::ptr_eq(node.parent.borrow().as_ref().unwrap(), &parent));
292        }
293
294        #[test]
295        fn test_compress_path_root() {
296            let node = DsuNode::new(10);
297
298            node.compress_path();
299
300            assert!(node.parent.borrow().is_none());
301        }
302
303        #[test]
304        fn test_compress_path_root_child() {
305            let mut node = DsuNode::new(10);
306            let root = Rc::new(DsuNode::new(20));
307            node.parent = RefCell::new(Some(root.clone()));
308
309            node.compress_path();
310
311            assert!(node.parent.borrow().is_some());
312            assert!(Rc::ptr_eq(node.parent.borrow().as_ref().unwrap(), &root));
313        }
314
315        #[test]
316        fn test_compress_path_deep() {
317            let nodes = create_link_tree();
318
319            nodes.last().unwrap().compress_path();
320
321            assert_path_compressed(&nodes);
322        }
323    }
324
325    mod dsu_root_tests {
326        use super::*;
327
328        #[test]
329        fn test_new() {
330            let ptr = DsuRoot::new(10);
331
332            assert!(ptr.node.is_root());
333            assert_eq!(ptr.node.value, 10);
334        }
335
336        #[test]
337        fn test_same() {
338            let mut ptr_1 = DsuRoot::new(10);
339            let mut ptr_2 = DsuRoot::new(20);
340
341            assert!(!DsuRoot::same(&mut ptr_1, &mut ptr_2));
342
343            ptr_1.node.set_parent(ptr_2.node.clone());
344
345            assert!(DsuRoot::same(&mut ptr_1, &mut ptr_2));
346        }
347
348        #[test]
349        fn test_value_basic() {
350            let mut ptr = DsuRoot::new(10);
351            assert_eq!(*ptr.value(), 10);
352        }
353
354        #[test]
355        fn test_value_merged() {
356            let mut ptr = DsuRoot::new(10);
357            ptr.node.set_parent(Rc::new(DsuNode::new(20)));
358
359            assert_eq!(*ptr.value(), 20);
360        }
361
362        #[test]
363        fn test_merge_into_basic() {
364            let mut ptr = DsuRoot::new(10);
365            let mut root = DsuRoot::new(20);
366
367            ptr.merge_into(&mut root);
368
369            assert!(DsuRoot::same(&mut ptr, &mut root));
370            assert_eq!(*ptr.value(), 20);
371        }
372
373        #[test]
374        fn test_merge_into_root() {
375            let mut ptr = DsuRoot::new(10);
376            let mut root = DsuRoot::new(20);
377
378            ptr.node.set_parent(root.node.clone());
379
380            ptr.merge_into(&mut root);
381
382            assert_eq!(*ptr.value(), 20);
383        }
384
385        #[test]
386        fn test_merge_into_child() {
387            let mut ptr = DsuRoot::new(10);
388            let mut root = DsuRoot::new(20);
389
390            ptr.node.set_parent(root.node.clone());
391
392            root.merge_into(&mut ptr);
393
394            assert_eq!(*root.value(), 20);
395        }
396
397        #[test]
398        fn test_move_to_root() {
399            let nodes = create_link_tree();
400            let root = nodes[0].clone();
401
402            let mut ptr = DsuRoot {
403                node: nodes.last().unwrap().clone(),
404            };
405            ptr.move_to_root();
406
407            assert!(Rc::ptr_eq(&ptr.node, &root));
408
409            assert_path_compressed(&nodes);
410        }
411    }
412}