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}