├── .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 | 
4 | 
5 | 
6 | 
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