struct UnionFind {
parent: Vec<usize>,
}
impl UnionFind {
fn new(size: usize) -> Self {
UnionFind { parent: (0..size).collect() }
}
fn find(&mut self, x: usize) -> usize {
if self.parent[x] != x {
self.parent[x] = self.find(self.parent[x]);
}
self.parent[x]
}
fn union(&mut self, a: usize, b: usize) {
let root_a = self.find(a);
let root_b = self.find(b);
if root_a != root_b {
self.parent[root_b] = root_a;
}
}
}
fn main() {
let mut uf = UnionFind::new(5);
uf.union(0, 1);
uf.union(1, 2);
println!("{}", uf.find(2));
println!("{:?}", uf.parent);
}The find function returns the root of the set. After union(0,1) and union(1,2), the root of 2 is 0. The path compression updates parents of 1 and 2 to 0. So, uf.find(2) returns 0, and the parent array becomes [0, 0, 0, 3, 4].
Path compression makes nodes point directly to the root, flattening the tree and reducing its height. This speeds up future find operations.
struct UnionFind {
parent: Vec<usize>,
}
impl UnionFind {
fn new(size: usize) -> Self {
UnionFind { parent: (0..size).collect() }
}
fn find(&mut self, x: usize) -> usize {
if self.parent[x] != x {
self.find(self.parent[x]);
}
self.parent[x]
}
}
fn main() {
let mut uf = UnionFind::new(3);
uf.parent[1] = 0;
println!("{}", uf.find(1));
}The find function calls itself recursively but does not update the parent[x] with the result. This causes infinite recursion when parent[x] != x, leading to stack overflow.
struct UnionFind {
parent: Vec<usize>,
}
impl UnionFind {
fn new(size: usize) -> Self {
UnionFind { parent: (0..size).collect() }
}
fn find(&mut self, x: usize) -> usize {
if self.parent[x] != x {
self.parent[x] = self.find(self.parent[x]);
}
self.parent[x]
}
fn union(&mut self, a: usize, b: usize) {
let root_a = self.find(a);
let root_b = self.find(b);
if root_a != root_b {
self.parent[root_b] = root_a;
}
}
}
fn main() {
let mut uf = UnionFind::new(6);
uf.union(0, 1);
uf.union(1, 2);
uf.union(3, 4);
uf.union(4, 5);
uf.union(2, 5);
uf.find(5);
println!("{:?}", uf.parent);
}After unions, sets {0,1,2} and {3,4,5} are formed. Then union(2,5) connects these sets with root 0. The find(5) call compresses path, updating parent of 5 to 0. Parent of 4 remains 3 because find(4) was not called. So parent array is [0,0,0,3,3,0].
The combination of path compression and union by rank gives an amortized time complexity of O(α(n)) per find operation, where α(n) is the inverse Ackermann function, which grows very slowly and is practically constant.
