fera_unionfind/lib.rs
1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5#![doc(html_root_url="https://docs.rs/fera-unionfind/0.1.0/")]
6
7//! Union-find ([disjoint-set]) data structure implementation.
8//!
9//! This implementation use path compression and rank heuristic. With default type parameters the
10//! parents and ranks are stored in a [`std::collections::HashMap`]. If the keys are in range
11//! `0..n`, use [`UnionFindRange`].
12//!
13//! The keys should implement `Copy`. If the keys does not implement `Copy`, references to the keys
14//! stored elsewhere should be used.
15//!
16//! This crate can be used through [`fera`] crate.
17//!
18//!
19//! # Examples
20//!
21//! ```
22//! use fera_unionfind::UnionFind;
23//!
24//! // Explicit type to make it clear
25//! let mut s: UnionFind<&'static str> = UnionFind::new();
26//!
27//! s.make_set("red");
28//! s.make_set("green");
29//! s.make_set("blue");
30//!
31//! assert_eq!(3, s.num_sets());
32//! assert!(!s.in_same_set("red", "green"));
33//! assert!(!s.in_same_set("red", "blue"));
34//!
35//! s.union("red", "blue");
36//!
37//! assert_eq!(2, s.num_sets());
38//! assert!(!s.in_same_set("red", "green"));
39//! assert!(s.in_same_set("red", "blue"));
40//! ```
41//!
42//! Using non `Copy` keys.
43//!
44//! ```
45//! use fera_unionfind::UnionFind;
46//!
47//! // This is invalid. String does not implement copy.
48//! // let mut x: UnionFind<String> = UnionFind::new();
49//! // Lets store the keys in a vector and use references (references are Copy).
50//! let v = vec!["red".to_string(), "green".to_string(), "blue".to_string()];
51//!
52//! // The type of s is Union<&'a String> where 'a is the lifetime of v.
53//! let mut s = UnionFind::new();
54//!
55//! s.make_set(&v[0]);
56//! s.make_set(&v[1]);
57//! s.make_set(&v[2]);
58//!
59//! assert_eq!(3, s.num_sets());
60//! assert!(!s.in_same_set(&v[0], &v[1]));
61//! assert!(!s.in_same_set(&v[0], &v[2]));
62//!
63//! s.union(&v[0], &v[2]);
64//!
65//! assert_eq!(2, s.num_sets());
66//! assert!(!s.in_same_set(&v[0], &v[1]));
67//! assert!(s.in_same_set(&v[0], &v[2]));
68//! ```
69//!
70//! Using keys in the range `0..n`.
71//!
72//! ```
73//! use fera_unionfind::UnionFindRange;
74//!
75//! let mut s = UnionFindRange::with_keys_in_range(..5);
76//!
77//! // It is not necessary to call UnionFind::make_set
78//!
79//! assert_eq!(5, s.num_sets());
80//! assert!(!s.in_same_set(0, 1));
81//! assert!(!s.in_same_set(0, 2));
82//!
83//! s.union(0, 2);
84//!
85//! assert_eq!(4, s.num_sets());
86//! assert!(!s.in_same_set(0, 1));
87//! assert!(s.in_same_set(0, 2));
88//!
89//! s.reset();
90//! assert_eq!(5, s.num_sets());
91//! ```
92//!
93//!
94//! [disjoint-set]: https://en.wikipedia.org/wiki/Disjoint-set_data_structure
95//! [`fera`]: https://docs.rs/fera
96//! [`std::collections::HashMap`]: https://doc.rust-lang.org/stable/std/collections/struct.HashMap.html
97//! [`UnionFindRange`]: type.UnionFindRange.html
98
99#![cfg_attr(feature = "cargo-clippy", allow(inline_always))]
100
101use std::collections::HashMap;
102use std::collections::hash_map::RandomState;
103use std::hash::{BuildHasher, Hash};
104use std::marker::PhantomData;
105use std::ops::{Index, IndexMut, RangeTo};
106
107/// [`UnionFind`] with keys in range `0..n`.
108///
109/// [`UnionFind`]: struct.UnionFind.html
110pub type UnionFindRange = UnionFind<usize, Vec<usize>, Vec<usize>>;
111
112/// A union-find ([disjoint-set]) struct.
113///
114/// [disjoint-set]: https://en.wikipedia.org/wiki/Disjoint-set_data_structure
115#[derive(Clone)]
116pub struct UnionFind<Key, Parent = IndexedHashMap<Key, Key>, Rank = IndexedHashMap<Key, usize>>
117 where Key: Copy + PartialEq,
118 Parent: IndexMut<Key, Output = Key>,
119 Rank: IndexMut<Key, Output = usize>
120{
121 parent: Parent,
122 rank: Rank,
123 num_sets: usize,
124 _marker: PhantomData<Key>,
125}
126
127impl<Key, Parent, Rank> UnionFind<Key, Parent, Rank>
128 where Key: Copy + PartialEq,
129 Parent: IndexMut<Key, Output = Key>,
130 Rank: IndexMut<Key, Output = usize>
131{
132 /// Creates a new `UnionFind`.
133 #[doc(hidden)]
134 pub fn with_parent_rank_num_sets(parent: Parent, rank: Rank, num_sets: usize) -> Self {
135 UnionFind {
136 parent: parent,
137 rank: rank,
138 num_sets: num_sets,
139 _marker: PhantomData,
140 }
141 }
142
143 /// Adds the key in it's own set. The number of sets is increased by 1.
144 ///
145 /// It's undefined behavior to call this method with a key that is already in a set.
146 pub fn make_set(&mut self, x: Key) {
147 // TODO: if x has a parent?
148 self.set_parent(x, x);
149 self.set_rank(x, 0);
150 self.num_sets += 1;
151 }
152
153 /// Joins the sets with the keys `x` and `y`. The number of sets is decreased by 1.
154 ///
155 /// # Panics
156 ///
157 /// If `x` or `y` is not in any set or if both are in the same set.
158 pub fn union(&mut self, x: Key, y: Key) {
159 let a = self.find_set(x);
160 let b = self.find_set(y);
161 assert!(a != b);
162 self.link(a, b);
163 }
164
165 /// Returns `true` if `x` and `y` is in the same set, otherwise `false`.
166 ///
167 /// # Panics
168 ///
169 /// If `x` or `y` is not in any set.
170 pub fn in_same_set(&mut self, x: Key, y: Key) -> bool {
171 self.find_set(x) == self.find_set(y)
172 }
173
174 /// Returns the representative of the set that contains `x`.
175 ///
176 /// # Panics
177 ///
178 /// If `x` is not in any set.
179 pub fn find_set(&mut self, mut x: Key) -> Key {
180 while self.parent(x) != x {
181 let p = self.parent(self.parent(x));
182 self.set_parent(x, p);
183 x = p;
184 }
185 self.parent(x)
186 }
187
188 /// Returns the number of distinct sets.
189 pub fn num_sets(&self) -> usize {
190 self.num_sets
191 }
192
193 fn link(&mut self, x: Key, y: Key) {
194 self.num_sets -= 1;
195 if self.rank(x) > self.rank(y) {
196 self.set_parent(y, x);
197 } else {
198 self.set_parent(x, y);
199 if self.rank(x) == self.rank(y) {
200 self.inc_rank(y);
201 }
202 }
203 }
204
205 #[inline(always)]
206 fn inc_rank(&mut self, k: Key) {
207 self.rank[k] += 1;
208 }
209
210 #[inline(always)]
211 fn rank(&self, k: Key) -> usize {
212 self.rank[k]
213 }
214
215 #[inline(always)]
216 fn set_rank(&mut self, k: Key, rank: usize) {
217 self.rank[k] = rank;
218 }
219
220 #[inline(always)]
221 fn parent(&self, k: Key) -> Key {
222 self.parent[k]
223 }
224
225 #[inline(always)]
226 fn set_parent(&mut self, k: Key, p: Key) {
227 self.parent[k] = p;
228 }
229}
230
231impl<K: Copy + Hash + Eq> UnionFind<K> {
232 /// Creates a new [`UnionFind`].
233 ///
234 /// [`UnionFind`]: struct.UnionFind.html
235 pub fn new() -> Self {
236 UnionFind::with_parent_rank_num_sets(
237 IndexedHashMap::new_parent_with_hasher(RandomState::new()),
238 IndexedHashMap::new_rank_with_hasher(RandomState::new()),
239 0,
240 )
241 }
242}
243
244impl UnionFindRange {
245 /// Creates a new `UnionFindRange` with keys in `range`.
246 pub fn with_keys_in_range(range: RangeTo<usize>) -> Self {
247 UnionFind::with_parent_rank_num_sets((0..range.end).collect(),
248 vec![0; range.end],
249 range.end)
250 }
251
252 /// Reset the struct putting each key in it's own set.
253 // TODO: how to implement this method for any UnionFind?
254 pub fn reset(&mut self) {
255 let n = self.parent.len();
256 for i in 0..n {
257 self.parent[i] = i;
258 self.rank[i] = 0;
259 }
260 self.num_sets = n;
261 }
262}
263
264/// This implements a map that can be used with [`UnionFind`].
265///
266/// [`UnionFind`]: struct.UnionFind.html
267///
268/// TODO: add docs of how to use a different hasher
269pub struct IndexedHashMap<K, V, S = RandomState>
270 where K: Copy + Hash + Eq,
271 S: BuildHasher
272{
273 map: HashMap<K, V, S>,
274 default: fn(&K) -> V,
275}
276
277impl<K, S> IndexedHashMap<K, K, S>
278 where K: Copy + Hash + Eq,
279 S: BuildHasher
280{
281 pub fn new_parent_with_hasher(hasher: S) -> Self {
282 IndexedHashMap {
283 map: HashMap::with_hasher(hasher),
284 default: K::clone,
285 }
286 }
287}
288
289impl<K, S> IndexedHashMap<K, usize, S>
290 where K: Copy + Hash + Eq,
291 S: BuildHasher
292{
293 pub fn new_rank_with_hasher(hasher: S) -> Self {
294 fn zero<K: Clone>(_: &K) -> usize {
295 0
296 }
297
298 IndexedHashMap {
299 map: HashMap::with_hasher(hasher),
300 default: zero,
301 }
302 }
303}
304
305impl<K, V> Index<K> for IndexedHashMap<K, V>
306 where K: Copy + Hash + Eq
307{
308 type Output = V;
309 fn index(&self, index: K) -> &Self::Output {
310 &self.map[&index]
311 }
312}
313
314impl<K, V> IndexMut<K> for IndexedHashMap<K, V>
315 where K: Copy + Hash + Eq
316{
317 fn index_mut(&mut self, index: K) -> &mut Self::Output {
318 let f = self.default;
319 self.map.entry(index).or_insert_with(|| f(&index))
320 }
321}
322
323
324#[cfg(test)]
325mod tests {
326 use *;
327
328 type UF = UnionFind<usize, Vec<usize>, Vec<usize>>;
329
330 fn check(ds: &mut UF, num_sets: usize, groups: &[&[usize]]) {
331 assert_eq!(num_sets, ds.num_sets());
332 for group in groups {
333 for &a in *group {
334 assert!(ds.in_same_set(group[0], a));
335 }
336 }
337 }
338
339 #[test]
340 fn unionfind1() {
341 let mut ds = UnionFind::with_keys_in_range(..5);
342 check(&mut ds, 5, &[&[]]);
343 ds.union(0, 2);
344 check(&mut ds, 4, &[&[0, 2]]);
345 ds.union(1, 3);
346 check(&mut ds, 3, &[&[0, 2], &[1, 3]]);
347 ds.union(2, 4);
348 check(&mut ds, 2, &[&[0, 2, 4], &[1, 3]]);
349 ds.union(3, 4);
350 check(&mut ds, 1, &[&[0, 2, 4, 1, 3]]);
351 }
352
353 #[test]
354 fn unionfind2() {
355 let mut ds = UnionFind::with_keys_in_range(..16);
356 ds.union(0, 1);
357 ds.union(2, 3);
358 ds.union(4, 5);
359 ds.union(6, 7);
360
361 ds.union(1, 2);
362 ds.union(5, 6);
363 ds.union(3, 7);
364
365 ds.union(8, 9);
366 ds.union(10, 11);
367 ds.union(12, 13);
368 ds.union(14, 15);
369
370 ds.union(9, 10);
371 ds.union(13, 14);
372 ds.union(11, 15);
373
374 ds.union(7, 15);
375
376 check(&mut ds,
377 1,
378 &[&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]]);
379 }
380}