#[derive(Debug, Clone)]
pub struct UnionFind {
parent: Vec<u32>,
rank: Vec<u8>,
}
impl UnionFind {
#[must_use]
pub fn new(n: usize) -> Self {
Self {
parent: (0..n as u32).collect(),
rank: vec![0; n],
}
}
pub fn reserve(&mut self, additional: usize) {
self.parent.reserve(additional);
self.rank.reserve(additional);
}
pub fn grow_to(&mut self, n: usize) {
let old = self.parent.len();
if n <= old {
return;
}
self.parent.extend(old as u32..n as u32);
self.rank.resize(n, 0);
}
pub fn find(&mut self, mut x: u32) -> u32 {
if x as usize >= self.parent.len() {
return x;
}
while self.parent[x as usize] != x {
let gp = self.parent[self.parent[x as usize] as usize];
self.parent[x as usize] = gp;
x = gp;
}
x
}
pub fn union(&mut self, x: u32, y: u32) -> u32 {
let rx = self.find(x);
let ry = self.find(y);
if rx == ry {
return rx;
}
let max_idx = (rx.max(ry) as usize).saturating_add(1);
self.grow_to(max_idx);
let (root, child) = if self.rank[rx as usize] >= self.rank[ry as usize] {
(rx, ry)
} else {
(ry, rx)
};
self.parent[child as usize] = root;
if self.rank[rx as usize] == self.rank[ry as usize] {
self.rank[root as usize] = self.rank[root as usize].saturating_add(1);
}
root
}
pub fn same(&mut self, x: u32, y: u32) -> bool {
self.find(x) == self.find(y)
}
#[must_use]
pub const fn len(&self) -> usize {
self.parent.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.parent.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn singleton_classes() {
let mut uf = UnionFind::new(5);
for i in 0..5u32 {
assert_eq!(uf.find(i), i);
}
}
#[test]
fn union_merges_classes() {
let mut uf = UnionFind::new(6);
uf.union(0, 1);
uf.union(2, 3);
assert!(uf.same(0, 1));
assert!(uf.same(2, 3));
assert!(!uf.same(0, 2));
uf.union(1, 2);
assert!(uf.same(0, 3));
}
#[test]
fn path_compression_gives_flat_structure() {
let mut uf = UnionFind::new(8);
for i in 0..7u32 {
uf.union(i, i + 1);
}
let root = uf.find(0);
for i in 0..8u32 {
assert_eq!(uf.find(i), root);
}
}
#[test]
fn grow_to_extends_cleanly() {
let mut uf = UnionFind::new(3);
uf.grow_to(8);
assert_eq!(uf.len(), 8);
for i in 3..8u32 {
assert_eq!(uf.find(i), i, "new singletons");
}
}
}