graph_collections/disjoint_set.rs
1/// Union-Find (disjoint set union) with **union-by-rank**.
2///
3/// Supports O(log n) `find` and `union` in this phase. Elements are represented as integer indices `0..n`. Create a `DisjointSet` of size `n`, then call `union` to merge components and `find` to discover which component an element belongs to.
4///
5/// # Examples
6///
7/// ```
8/// use graph_collections::DisjointSet;
9///
10/// let mut ds = DisjointSet::new(5); // indices 0..5
11/// assert_eq!(ds.count(), 5);
12///
13/// ds.union(0, 1);
14/// ds.union(1, 2);
15/// assert!(ds.connected(0, 2));
16/// assert!(!ds.connected(0, 3));
17/// assert_eq!(ds.count(), 3); // {0,1,2}, {3}, {4}
18/// ```
19#[derive(Debug, Clone)]
20pub struct DisjointSet {
21 parent: Vec<usize>,
22 rank: Vec<usize>,
23 count: usize, // number of disjoint sets
24}
25
26impl DisjointSet {
27 /// Creates a `DisjointSet` with `n` singleton sets, one per index `0..n`.
28 ///
29 /// Each element is initially its own parent (root), and every rank starts
30 /// at 0.
31 ///
32 /// # Panics
33 ///
34 /// Does not panic, but `n = 0` gives an empty structure where every
35 /// operation on any index would panic at the index.
36 ///
37 /// # Examples
38 ///
39 /// ```
40 /// use graph_collections::DisjointSet;
41 ///
42 /// let ds = DisjointSet::new(4);
43 /// assert_eq!(ds.count(), 4);
44 /// ```
45 pub fn new(n: usize) -> Self {
46 Self {
47 parent: (0..n).collect(),
48 rank: vec![0; n],
49 count: n,
50 }
51 }
52}
53
54impl DisjointSet {
55 /// Returns the **representative (root)** of the set containing `x`.
56 ///
57 /// Uses iterative root-following without path compression. Two elements are in the same component iff `find(a) == find(b)`.
58 ///
59 /// # Panics
60 ///
61 /// Panics if `x >= n` (out of bounds).
62 ///
63 /// # Examples
64 ///
65 /// ```
66 /// use graph_collections::DisjointSet;
67 ///
68 /// let mut ds = DisjointSet::new(3);
69 /// ds.union(0, 1);
70 /// // Both 0 and 1 have the same root; 2 has its own.
71 /// assert_eq!(ds.find(0), ds.find(1));
72 /// assert_ne!(ds.find(0), ds.find(2));
73 /// ```
74 pub fn find(&mut self, mut x: usize) -> usize {
75 // Walk to the root without modifying the tree.
76 while self.parent[x] != x {
77 x = self.parent[x];
78 }
79 x
80 }
81
82 /// Merges the sets containing `x` and `y`.
83 ///
84 /// Uses **union-by-rank**: the root with the lower rank is attached under
85 /// the root with the higher rank, keeping trees shallow. When ranks are
86 /// equal the second root is attached under the first and its rank increments.
87 ///
88 /// Returns `true` if the two elements were in **different** sets (a merge
89 /// actually happened), or `false` if they were already in the same set.
90 ///
91 /// # Panics
92 ///
93 /// Panics if `x >= n` or `y >= n`.
94 ///
95 /// # Examples
96 ///
97 /// ```
98 /// use graph_collections::DisjointSet;
99 ///
100 /// let mut ds = DisjointSet::new(4);
101 /// assert!(ds.union(0, 1)); // new merge
102 /// assert!(!ds.union(0, 1)); // already connected
103 /// assert_eq!(ds.count(), 3);
104 /// ```
105 pub fn union(&mut self, x: usize, y: usize) -> bool {
106 let rx = self.find(x);
107 let ry = self.find(y);
108
109 if rx == ry {
110 return false; // already in the same component
111 }
112
113 // Attach smaller-rank tree under larger-rank tree.
114 match self.rank[rx].cmp(&self.rank[ry]) {
115 std::cmp::Ordering::Less => self.parent[rx] = ry,
116 std::cmp::Ordering::Greater => self.parent[ry] = rx,
117 std::cmp::Ordering::Equal => {
118 self.parent[ry] = rx;
119 self.rank[rx] += 1;
120 }
121 }
122
123 self.count -= 1;
124 true
125 }
126
127 /// Returns `true` if `x` and `y` belong to the same component.
128 ///
129 /// Equivalent to `find(x) == find(y)`.
130 ///
131 /// # Panics
132 ///
133 /// Panics if `x >= n` or `y >= n`.
134 ///
135 /// # Examples
136 ///
137 /// ```
138 /// use graph_collections::DisjointSet;
139 ///
140 /// let mut ds = DisjointSet::new(5);
141 /// ds.union(1, 3);
142 /// assert!(ds.connected(1, 3));
143 /// assert!(!ds.connected(1, 4));
144 /// ```
145 #[inline]
146 pub fn connected(&mut self, x: usize, y: usize) -> bool {
147 self.find(x) == self.find(y)
148 }
149
150 /// Returns the number of **disjoint sets** (connected components).
151 ///
152 /// Starts at `n` and decrements by one for each successful `union`.
153 ///
154 /// # Examples
155 ///
156 /// ```
157 /// use graph_collections::DisjointSet;
158 ///
159 /// let mut ds = DisjointSet::new(4);
160 /// assert_eq!(ds.count(), 4);
161 /// ds.union(0, 1);
162 /// assert_eq!(ds.count(), 3);
163 /// ds.union(2, 3);
164 /// assert_eq!(ds.count(), 2);
165 /// ds.union(0, 3);
166 /// assert_eq!(ds.count(), 1);
167 /// ```
168 #[must_use]
169 #[inline]
170 pub fn count(&self) -> usize {
171 self.count
172 }
173}
174
175impl DisjointSet {
176 /// Returns the total number of elements (size passed to [`DisjointSet::new`]).
177 ///
178 /// This is the **capacity** of the structure, not the number of components.
179 /// For the number of components use [`count`](DisjointSet::count).
180 ///
181 /// # Examples
182 ///
183 /// ```
184 /// use graph_collections::DisjointSet;
185 ///
186 /// let ds = DisjointSet::new(10);
187 /// assert_eq!(ds.size(), 10);
188 /// ```
189 #[must_use]
190 #[inline]
191 pub fn size(&self) -> usize {
192 self.parent.len()
193 }
194
195 /// Returns `true` if all elements belong to a single component.
196 ///
197 /// # Examples
198 ///
199 /// ```
200 /// use graph_collections::DisjointSet;
201 ///
202 /// let mut ds = DisjointSet::new(3);
203 /// assert!(!ds.is_fully_connected());
204 /// ds.union(0, 1);
205 /// ds.union(1, 2);
206 /// assert!(ds.is_fully_connected());
207 /// ```
208 #[inline]
209 pub fn is_fully_connected(&self) -> bool {
210 self.count == 1
211 }
212}