|
| 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)] |
| 20 | +pub struct DisjointSet { |
| 21 | + parent: Vec<usize>, |
| 22 | + rank: Vec<usize>, |
| 23 | + count: usize, // number of disjoint sets |
| 24 | +} |
| 25 | + |
| 26 | +impl 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 | + |
| 54 | +impl 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 | + |
| 175 | +impl 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 | +} |
0 commit comments