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}