use std::{
cell::Cell,
collections::{hash_map::Entry, HashMap},
};
#[allow(clippy::missing_inline_in_public_items)]
#[derive(Debug, Clone)]
pub struct DisjointSet {
parents: Vec<Cell<usize>>,
ranks: Vec<u8>,
}
impl Default for DisjointSet {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl DisjointSet {
#[inline]
#[must_use]
fn get_parent(&self, id: usize) -> usize {
self.parents[id].get()
}
#[inline]
fn set_parent(&self, id: usize, new: usize) {
self.parents[id].set(new);
}
#[inline]
#[must_use]
fn get_mut_rank(&mut self, id: usize) -> &mut u8 {
&mut self.ranks[id]
}
#[inline]
#[must_use]
pub fn root_of(&self, mut child: usize) -> usize {
let mut parent = self.get_parent(child);
if child == parent {
return child;
};
loop {
let grandparent = self.get_parent(parent);
if parent == grandparent {
return parent;
}
self.set_parent(child, grandparent);
child = parent;
parent = grandparent;
}
}
#[inline]
#[must_use]
pub fn with_len(len: usize) -> Self {
Self {
parents: (0..len).map(Cell::new).collect(),
ranks: vec![0; len],
}
}
#[inline]
#[must_use]
pub fn with_capacity(capacity: usize) -> Self {
Self {
parents: Vec::with_capacity(capacity),
ranks: Vec::with_capacity(capacity),
}
}
#[inline]
pub fn add_singleton(&mut self) -> usize {
let id = self.len();
self.parents.push(Cell::new(id));
self.ranks.push(0);
id
}
#[inline]
pub fn join(&mut self, first_element: usize, second_element: usize) -> bool {
fn slow_path(ds: &mut DisjointSet, first_element: usize, second_element: usize) -> bool {
let root_first = ds.root_of(first_element);
let root_second = ds.root_of(second_element);
if root_first == root_second {
return false;
}
let rank_second = *ds.get_mut_rank(root_second);
let rank_first = ds.get_mut_rank(root_first);
if *rank_first < rank_second {
ds.set_parent(root_first, root_second);
} else {
if *rank_first == rank_second {
*rank_first += 1;
}
ds.set_parent(root_second, root_first);
}
true
}
if self.get_parent(first_element) == self.get_parent(second_element) {
return false;
}
slow_path(self, first_element, second_element)
}
#[must_use]
#[inline]
pub fn is_joined(&self, first_element: usize, second_element: usize) -> bool {
self.root_of(first_element) == self.root_of(second_element)
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.parents.len()
}
#[must_use]
#[inline]
pub fn is_empty(&self) -> bool {
self.parents.is_empty()
}
#[must_use]
#[inline]
#[allow(clippy::missing_const_for_fn)]
pub fn new() -> Self {
Self {
parents: Vec::new(),
ranks: Vec::new(),
}
}
#[inline]
pub fn clear(&mut self) {
self.parents.clear();
self.ranks.clear();
}
#[must_use]
#[allow(clippy::missing_inline_in_public_items)]
pub fn sets(&self) -> Vec<Vec<usize>> {
let mut result = Vec::new();
let mut root_to_result_id = HashMap::new();
for index in 0..self.len() {
let root = self.root_of(index);
let &mut result_id = root_to_result_id.entry(root).or_insert_with(|| {
let id = result.len();
result.push(Vec::with_capacity(1));
id
});
result[result_id].push(index);
}
result
}
}
impl PartialEq for DisjointSet {
#[must_use]
#[allow(clippy::missing_inline_in_public_items)]
fn eq(&self, other: &Self) -> bool {
if self.len() != other.len() {
return false;
}
let mut self_root_to_other_root = HashMap::with_capacity(self.len());
for i in 0..self.len() {
let self_root = self.root_of(i);
let other_root = other.root_of(i);
match self_root_to_other_root.entry(self_root) {
Entry::Occupied(entry) => {
if other_root != *entry.get() {
return false;
}
}
Entry::Vacant(entry) => {
entry.insert(other_root);
}
}
}
true
}
}
impl Eq for DisjointSet {}
#[cfg(test)]
mod test {
use crate::DisjointSet;
#[test]
fn join_returns_false_even_if_immediate_parent_check_fails() {
let mut ds = DisjointSet::with_len(4);
ds.join(0, 1);
ds.join(2, 3);
ds.join(2, 0);
assert_ne!(ds.parents[1], ds.parents[3]);
assert!(!ds.join(1, 3));
}
#[test]
fn clear_removes_elements_without_removing_capacity() {
let mut set = DisjointSet::new();
set.add_singleton();
set.add_singleton();
let capacity = set.parents.capacity();
set.clear();
assert_eq!(set.parents.capacity(), capacity);
}
}