├── .gitignore ├── .idea ├── .gitignore ├── dsu-tree.iml ├── modules.xml └── vcs.xml ├── Cargo.toml ├── LICENSE ├── README.md ├── examples └── kruskal.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/dsu-tree.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "dsu-tree" 3 | authors = ["Sirui Mu "] 4 | version = "0.1.0" 5 | edition = "2018" 6 | license-file = "LICENSE" 7 | description = "A non-invasive disjoint-set-like data structure implementation" 8 | homepage = "https://github.com/Lancern/dsu-tree" 9 | documentation = "https://docs.rs/dsu-tree/0.1.0/dsu-tree" 10 | repository = "https://github.com/Lancern/dsu-tree" 11 | readme = "README.md" 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [dependencies] 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Sirui Mu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dsu-tree 2 | 3 | ![version-badge](https://img.shields.io/crates/v/dsu-tree) 4 | ![license-badge](https://img.shields.io/crates/l/dsu-tree) 5 | ![build-status](https://img.shields.io/docsrs/dsu-tree) 6 | ![downloads-badge](https://img.shields.io/crates/dv/dsu-tree) 7 | 8 | A non-invasive implementation of a **disjoint-set tree** data structure, written in Rust. 9 | 10 | # Disjoint-Set Tree 11 | 12 | **[Disjoint sets](https://en.wikipedia.org/wiki/Disjoint-set_data_structure) data structure**, or **DSU**, or **union-find data structure**, or **merge- find set**, is a data structure that stores a collection of disjoint sets. It provides operations for adding new sets, merging sets (equivalent to replacing the sets with the union of them) and finding the representative member of a set. DSU is very useful in implementing algorithms like [minimum spanning tree](https://en.wikipedia.org/wiki/Minimum_spanning_tree) and more. 13 | 14 | DSU can be implemented with its extreme efficiency by using **disjoint-set trees**. Disjoint-set trees are actually a forest in which each node represents a set and each tree represents the union of sets that are merged together. The three DSU operations can be implemented as follows: 15 | - Adding new sets: Easy. Just add new nodes to the forest and it's done. The new nodes are 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 just change the parent node of `A` to `B` and it's done. In real implementations, some corner cases need to be considered, such as merging a set into itself. 17 | - Finding the representative member of a set: Each tree within the disjoint-set trees represents a set. The representative member of a set can be chosen to be the representative member of the set corresponding to the root node of the tree. 18 | 19 | # This Crate 20 | 21 | Rather than implementing a disjoint-set data structure, this crate provides the implementation of the underlying disjoint-set tree data structure. 22 | 23 | For the usage of this library, please refer to the [documentation](https://docs.rs/dsu-tree/0.1.0/dsu-tree). 24 | 25 | # License 26 | 27 | This library is open-sourced under [MIT License](./LICENSE). 28 | -------------------------------------------------------------------------------- /examples/kruskal.rs: -------------------------------------------------------------------------------- 1 | extern crate dsu_tree; 2 | 3 | use std::cmp::{Ordering, Reverse}; 4 | use std::collections::BinaryHeap; 5 | use std::cell::RefCell; 6 | use dsu_tree::DsuRoot; 7 | 8 | struct Graph { 9 | edges: Vec, 10 | } 11 | 12 | impl Graph { 13 | fn new() -> Self { 14 | Self { 15 | edges: Vec::new(), 16 | } 17 | } 18 | 19 | fn add_edge(mut self, nodes: [usize; 2], weight: u32) -> Self { 20 | self.edges.push(Edge { nodes, weight }); 21 | self 22 | } 23 | 24 | fn take_edges(self) -> Vec { 25 | self.edges 26 | } 27 | } 28 | 29 | struct Edge { 30 | nodes: [usize; 2], 31 | weight: u32, 32 | } 33 | 34 | impl Eq for Edge {} 35 | 36 | impl Ord for Edge { 37 | fn cmp(&self, other: &Self) -> Ordering { 38 | self.weight.cmp(&other.weight) 39 | } 40 | } 41 | 42 | impl PartialEq for Edge { 43 | fn eq(&self, other: &Self) -> bool { 44 | self.weight == other.weight 45 | } 46 | } 47 | 48 | impl PartialOrd for Edge { 49 | fn partial_cmp(&self, other: &Self) -> Option { 50 | self.weight.partial_cmp(&other.weight) 51 | } 52 | } 53 | 54 | struct Dsu { 55 | roots: Vec>>, 56 | } 57 | 58 | impl Dsu { 59 | fn new() -> Self { 60 | Self { 61 | roots: Vec::new(), 62 | } 63 | } 64 | 65 | fn add_root(&mut self, value: usize) { 66 | self.roots.push(RefCell::new(DsuRoot::new(value))); 67 | } 68 | 69 | fn find(&self, id: usize) -> usize { 70 | *self.roots[id].borrow_mut().value() 71 | } 72 | 73 | fn merge(&mut self, first: usize, second: usize) { 74 | if first == second { 75 | return; 76 | } 77 | 78 | let mut first_root = self.roots[first].borrow_mut(); 79 | let mut second_root = self.roots[second].borrow_mut(); 80 | first_root.merge_into(&mut second_root); 81 | } 82 | } 83 | 84 | fn find_mst(nodes: usize, edges: Vec) -> u32 { 85 | let mut edges_heap = BinaryHeap::new(); 86 | for e in edges { 87 | edges_heap.push(Reverse(e)); 88 | } 89 | 90 | let mut dsu = Dsu::new(); 91 | for i in 0..nodes { 92 | dsu.add_root(i); 93 | } 94 | 95 | let mut mst_weight = 0u32; 96 | let mut edge_count = 0usize; 97 | while edge_count < nodes - 1 { 98 | let edge = edges_heap.pop().unwrap().0; 99 | if dsu.find(edge.nodes[0]) == dsu.find(edge.nodes[1]) { 100 | continue; 101 | } 102 | 103 | mst_weight += edge.weight; 104 | edge_count += 1; 105 | 106 | dsu.merge(edge.nodes[0], edge.nodes[1]); 107 | } 108 | 109 | mst_weight 110 | } 111 | 112 | fn main() { 113 | let graph_edges = Graph::new() 114 | .add_edge([0, 1], 1) 115 | .add_edge([0, 3], 4) 116 | .add_edge([0, 4], 3) 117 | .add_edge([1, 3], 4) 118 | .add_edge([1, 4], 2) 119 | .add_edge([2, 4], 4) 120 | .add_edge([2, 5], 5) 121 | .add_edge([3, 4], 4) 122 | .add_edge([4, 5], 7) 123 | .take_edges(); 124 | let mst_weight = find_mst(6, graph_edges); 125 | assert_eq!(mst_weight, 16); 126 | println!("ok"); 127 | } 128 | -------------------------------------------------------------------------------- /src/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 | 50 | extern crate alloc; 51 | 52 | use alloc::rc::Rc; 53 | use 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(Debug)] 63 | pub struct DsuRoot { 64 | node: Rc>, 65 | } 66 | 67 | // DsuRoot: Clone is fine even if T isn't Clone 68 | impl Clone for DsuRoot { 69 | fn clone(&self) -> Self { 70 | Self { 71 | node: self.node.clone(), 72 | } 73 | } 74 | } 75 | 76 | impl DsuRoot { 77 | /// Create a new DSU tree node and attach a `DsuRoot` smart pointer to the new node. 78 | /// 79 | /// The new DSU tree node contains the given value. 80 | pub fn new(value: T) -> Self { 81 | Self { 82 | node: Rc::new(DsuNode::new(value)), 83 | } 84 | } 85 | 86 | /// Determine whether the two `DsuRoot` smart pointers refer to the same tree. 87 | /// 88 | /// ``` 89 | /// use dsu_tree::DsuRoot; 90 | /// 91 | /// let mut dsu_1 = DsuRoot::new(10); 92 | /// let mut dsu_2 = DsuRoot::new(20); 93 | /// assert!(!DsuRoot::same(&mut dsu_1, &mut dsu_2)); 94 | /// 95 | /// dsu_1.merge_into(&mut dsu_2); 96 | /// assert!(DsuRoot::same(&mut dsu_1, &mut dsu_2)); 97 | /// ``` 98 | pub fn same(lhs: &mut Self, rhs: &mut Self) -> bool { 99 | lhs.move_to_root(); 100 | rhs.move_to_root(); 101 | Rc::ptr_eq(&lhs.node, &rhs.node) 102 | } 103 | 104 | /// Get the value contained in the DSU tree node pointed to by this `DsuRoot` smart pointer. 105 | /// 106 | /// This function requires `&mut self` since the `DsuRoot` smart pointer may move around over 107 | /// the DSU tree so that it eventually points to the root node. 108 | pub fn value(&mut self) -> &T { 109 | self.move_to_root(); 110 | &self.node.value 111 | } 112 | 113 | /// Merge two DSU trees into one. 114 | /// 115 | /// The first DSU tree is given through `self`. The second DSU tree is given through `another`. 116 | /// 117 | /// If initially, the two trees are the same tree, then this function does nothing. Otherwise, 118 | /// the parent node of the root node of the tree specified through `self` is set to the root 119 | /// node of the tree specified through `another`. 120 | pub fn merge_into(&mut self, another: &mut Self) { 121 | if Self::same(self, another) { 122 | return; 123 | } 124 | 125 | // After calling `same`, `self` and `another` should both point to the root of their DSU 126 | // tree. 127 | debug_assert!(self.node.is_root()); 128 | debug_assert!(another.node.is_root()); 129 | 130 | self.node.set_parent(another.node.clone()) 131 | } 132 | 133 | /// Move this smart pointer to make it point to the root node of the referring DSU tree. 134 | fn move_to_root(&mut self) { 135 | self.node.compress_path(); 136 | if !self.node.is_root() { 137 | self.node = self.node.parent().unwrap(); 138 | } 139 | 140 | debug_assert!(self.node.is_root()); 141 | } 142 | } 143 | 144 | /// The DSU tree node. 145 | #[derive(Debug)] 146 | struct DsuNode { 147 | parent: RefCell>>>, 148 | value: T, 149 | } 150 | 151 | impl DsuNode { 152 | /// Create a new DSU tree node that contains the given value. 153 | fn new(value: T) -> Self { 154 | Self { 155 | parent: RefCell::new(None), 156 | value, 157 | } 158 | } 159 | 160 | /// Determine whether this node is the root node. 161 | fn is_root(&self) -> bool { 162 | self.parent.borrow().is_none() 163 | } 164 | 165 | /// Get the parent node wrapped in an [`Option`]. 166 | /// 167 | /// If this node is the root node, this function returns [`None`]. 168 | /// 169 | /// [`Option`]: https://doc.rust-lang.org/core/option/enum.Option.html 170 | /// [`None`]: https://doc.rust-lang.org/core/option/enum.Option.html#variant.None 171 | fn parent(&self) -> Option>> { 172 | (*self.parent.borrow()).clone() 173 | } 174 | 175 | /// Set the parent node. 176 | //noinspection ALL 177 | fn set_parent(&self, parent: Rc>) { 178 | *self.parent.borrow_mut() = Some(parent); 179 | } 180 | 181 | /// Compress the path from this node to the root node of the tree. 182 | /// 183 | /// After path compression, the number of nodes along the path from this node to the root node 184 | /// cannot be greater than 2. That is, after path compression, one of the two conditions holds: 185 | /// * This node is initially the root node, and so it keeps to be the root node after path 186 | /// compression; 187 | /// * This node is not the root node initially, and after path compression, the parent node 188 | /// becomes the root node. 189 | fn compress_path(&self) { 190 | // Quick check to bail the trivial situations out in which: 191 | // * `self` is itself a root node; 192 | // * The parent node of `self` is a root node. 193 | // 194 | // In any of the two cases above, we don't have to do anything. 195 | let trivial = { 196 | let parent_borrow = self.parent.borrow(); 197 | match &*parent_borrow { 198 | Some(parent) => parent.is_root(), 199 | None => true, 200 | } 201 | }; 202 | if trivial { 203 | return; 204 | } 205 | 206 | // Do the path compression. 207 | let mut parent_borrow = self.parent.borrow_mut(); 208 | 209 | // First, compress the path from the parent to the root. After this step, the parent of the 210 | // parent node should be the root. 211 | let parent_node = parent_borrow.as_ref().unwrap(); 212 | parent_node.compress_path(); 213 | 214 | // Then, simply update the parent pointer. 215 | *parent_borrow = Some(parent_node.parent().unwrap()); 216 | } 217 | } 218 | 219 | #[cfg(test)] 220 | mod tests { 221 | use super::*; 222 | use alloc::vec::Vec; 223 | 224 | fn create_link_tree() -> Vec>> { 225 | let mut nodes = Vec::with_capacity(10); 226 | 227 | let root = Rc::new(DsuNode::new(0)); 228 | nodes.push(root.clone()); 229 | 230 | for i in 1..10usize { 231 | let mut node = DsuNode::new(i as i32); 232 | node.parent = RefCell::new(Some(nodes[i - 1].clone())); 233 | 234 | nodes.push(Rc::new(node)); 235 | } 236 | 237 | nodes 238 | } 239 | 240 | fn assert_path_compressed(nodes: &[Rc>]) { 241 | let root = nodes[0].clone(); 242 | for i in 1..nodes.len() { 243 | let parent = nodes[i].parent(); 244 | assert!(parent.is_some()); 245 | assert!(Rc::ptr_eq(parent.as_ref().unwrap(), &root)); 246 | } 247 | } 248 | 249 | mod dsu_node_tests { 250 | use super::*; 251 | 252 | #[test] 253 | fn test_new() { 254 | let node = DsuNode::new(10); 255 | 256 | assert!(node.parent.borrow().is_none()); 257 | assert_eq!(node.value, 10); 258 | } 259 | 260 | #[test] 261 | fn test_is_root_true() { 262 | let node = DsuNode::new(10); 263 | assert!(node.is_root()); 264 | } 265 | 266 | #[test] 267 | fn test_is_root_false() { 268 | let mut node = DsuNode::new(10); 269 | node.parent = RefCell::new(Some(Rc::new(DsuNode::new(20)))); 270 | 271 | assert!(!node.is_root()); 272 | } 273 | 274 | #[test] 275 | fn test_parent_root() { 276 | let node = DsuNode::new(10); 277 | 278 | assert!(node.parent().is_none()); 279 | } 280 | 281 | #[test] 282 | fn test_parent_non_root() { 283 | let mut node = DsuNode::new(10); 284 | let root = Rc::new(DsuNode::new(20)); 285 | node.parent = RefCell::new(Some(root.clone())); 286 | 287 | let node_parent = node.parent(); 288 | assert!(node_parent.is_some()); 289 | assert!(Rc::ptr_eq(node_parent.as_ref().unwrap(), &root)); 290 | } 291 | 292 | #[test] 293 | fn test_set_parent() { 294 | let node = DsuNode::new(10); 295 | let parent = Rc::new(DsuNode::new(20)); 296 | 297 | node.set_parent(parent.clone()); 298 | 299 | assert!(node.parent.borrow().is_some()); 300 | assert!(Rc::ptr_eq(node.parent.borrow().as_ref().unwrap(), &parent)); 301 | } 302 | 303 | #[test] 304 | fn test_compress_path_root() { 305 | let node = DsuNode::new(10); 306 | 307 | node.compress_path(); 308 | 309 | assert!(node.parent.borrow().is_none()); 310 | } 311 | 312 | #[test] 313 | fn test_compress_path_root_child() { 314 | let mut node = DsuNode::new(10); 315 | let root = Rc::new(DsuNode::new(20)); 316 | node.parent = RefCell::new(Some(root.clone())); 317 | 318 | node.compress_path(); 319 | 320 | assert!(node.parent.borrow().is_some()); 321 | assert!(Rc::ptr_eq(node.parent.borrow().as_ref().unwrap(), &root)); 322 | } 323 | 324 | #[test] 325 | fn test_compress_path_deep() { 326 | let nodes = create_link_tree(); 327 | 328 | nodes.last().unwrap().compress_path(); 329 | 330 | assert_path_compressed(&nodes); 331 | } 332 | } 333 | 334 | mod dsu_root_tests { 335 | use super::*; 336 | 337 | #[test] 338 | fn test_new() { 339 | let ptr = DsuRoot::new(10); 340 | 341 | assert!(ptr.node.is_root()); 342 | assert_eq!(ptr.node.value, 10); 343 | } 344 | 345 | #[test] 346 | fn test_clone() { 347 | struct NonCloneable; 348 | 349 | let mut ptr_1 = DsuRoot::new(NonCloneable); 350 | let mut ptr_2 = ptr_1.clone(); 351 | 352 | assert!(DsuRoot::same(&mut ptr_1, &mut ptr_2)); 353 | } 354 | 355 | #[test] 356 | fn test_same() { 357 | let mut ptr_1 = DsuRoot::new(10); 358 | let mut ptr_2 = DsuRoot::new(20); 359 | 360 | assert!(!DsuRoot::same(&mut ptr_1, &mut ptr_2)); 361 | 362 | ptr_1.node.set_parent(ptr_2.node.clone()); 363 | 364 | assert!(DsuRoot::same(&mut ptr_1, &mut ptr_2)); 365 | } 366 | 367 | #[test] 368 | fn test_value_basic() { 369 | let mut ptr = DsuRoot::new(10); 370 | assert_eq!(*ptr.value(), 10); 371 | } 372 | 373 | #[test] 374 | fn test_value_merged() { 375 | let mut ptr = DsuRoot::new(10); 376 | ptr.node.set_parent(Rc::new(DsuNode::new(20))); 377 | 378 | assert_eq!(*ptr.value(), 20); 379 | } 380 | 381 | #[test] 382 | fn test_merge_into_basic() { 383 | let mut ptr = DsuRoot::new(10); 384 | let mut root = DsuRoot::new(20); 385 | 386 | ptr.merge_into(&mut root); 387 | 388 | assert!(DsuRoot::same(&mut ptr, &mut root)); 389 | assert_eq!(*ptr.value(), 20); 390 | } 391 | 392 | #[test] 393 | fn test_merge_into_root() { 394 | let mut ptr = DsuRoot::new(10); 395 | let mut root = DsuRoot::new(20); 396 | 397 | ptr.node.set_parent(root.node.clone()); 398 | 399 | ptr.merge_into(&mut root); 400 | 401 | assert_eq!(*ptr.value(), 20); 402 | } 403 | 404 | #[test] 405 | fn test_merge_into_child() { 406 | let mut ptr = DsuRoot::new(10); 407 | let mut root = DsuRoot::new(20); 408 | 409 | ptr.node.set_parent(root.node.clone()); 410 | 411 | root.merge_into(&mut ptr); 412 | 413 | assert_eq!(*root.value(), 20); 414 | } 415 | 416 | #[test] 417 | fn test_move_to_root() { 418 | let nodes = create_link_tree(); 419 | let root = nodes[0].clone(); 420 | 421 | let mut ptr = DsuRoot { 422 | node: nodes.last().unwrap().clone(), 423 | }; 424 | ptr.move_to_root(); 425 | 426 | assert!(Rc::ptr_eq(&ptr.node, &root)); 427 | 428 | assert_path_compressed(&nodes); 429 | } 430 | } 431 | } 432 | --------------------------------------------------------------------------------