159 lines · 4.3 KB · Rust
RawDownload
// Copyright 2024 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. //! This module implements a [`UnionFind<T>`] type which can be used to//! efficiently calculate disjoint sets for any data type. use std::collections::HashMap;use std::hash::Hash; #[derive(Clone, Copy)]struct Node<T> {    root: T,    size: u32,} /// Implementation of the union-find algorithm:/// <https://en.wikipedia.org/wiki/Disjoint-set_data_structure>////// Joins disjoint sets by size to amortize cost.#[derive(Clone)]pub struct UnionFind<T> {    roots: HashMap<T, Node<T>>,} impl<T> Default for UnionFind<T>where    T: Copy + Eq + Hash,{    fn default() -> Self {        Self::new()    }} impl<T> UnionFind<T>where    T: Copy + Eq + Hash,{    /// Creates a new empty UnionFind data structure.    pub fn new() -> Self {        Self {            roots: HashMap::new(),        }    }     /// Returns the root identifying the union this item is a part of.    pub fn find(&mut self, item: T) -> T {        self.find_node(item).root    }     fn find_node(&mut self, item: T) -> Node<T> {        match self.roots.get(&item) {            Some(node) => {                if node.root != item {                    let new_root = self.find_node(node.root);                    self.roots.insert(item, new_root);                    new_root                } else {                    *node                }            }            None => {                let node = Node::<T> {                    root: item,                    size: 1,                };                self.roots.insert(item, node);                node            }        }    }     /// Unions the disjoint sets connected to `a` and `b`.    pub fn union(&mut self, a: T, b: T) {        let a = self.find_node(a);        let b = self.find_node(b);        if a.root == b.root {            return;        }         let new_node = Node::<T> {            root: if a.size < b.size { b.root } else { a.root },            size: a.size + b.size,        };        self.roots.insert(a.root, new_node);        self.roots.insert(b.root, new_node);    }} #[cfg(test)]mod tests {    use itertools::Itertools as _;     use super::*;     #[test]    fn test_basic() {        let mut union_find = UnionFind::<i32>::new();         // Everything starts as a singleton.        assert_eq!(union_find.find(1), 1);        assert_eq!(union_find.find(2), 2);        assert_eq!(union_find.find(3), 3);         // Make two pair sets. This implicitly adds node 4.        union_find.union(1, 2);        union_find.union(3, 4);        assert_eq!(union_find.find(1), union_find.find(2));        assert_eq!(union_find.find(3), union_find.find(4));        assert_ne!(union_find.find(1), union_find.find(3));         // Unioning the pairs gives everything the same root.        union_find.union(1, 3);        assert!(            [                union_find.find(1),                union_find.find(2),                union_find.find(3),                union_find.find(4),            ]            .iter()            .all_equal()        );    }     #[test]    fn test_union_by_size() {        let mut union_find = UnionFind::<i32>::new();         // Create a set of 3 and a set of 2.        union_find.union(1, 2);        union_find.union(2, 3);        union_find.union(4, 5);        let set3 = union_find.find(1);        let set2 = union_find.find(4);        assert_ne!(set3, set2);         // Merging them always chooses the larger set.        let mut large_first = union_find.clone();        large_first.union(1, 4);        assert_eq!(large_first.find(1), set3);        assert_eq!(large_first.find(4), set3);         let mut small_first = union_find.clone();        small_first.union(4, 1);        assert_eq!(small_first.find(1), set3);        assert_eq!(small_first.find(4), set3);    }}