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}