802-kruskals-mst — Kruskal's Minimum Spanning Tree
Tutorial Video
Text description (accessibility)
This video demonstrates the "802-kruskals-mst — Kruskal's Minimum Spanning Tree" functional Rust example. Difficulty level: Fundamental. Key concepts covered: Functional Programming. Kruskal's algorithm (1956) finds the MST by sorting all edges by weight and adding each edge if it doesn't create a cycle, using a Union-Find (Disjoint Set Union) data structure to detect cycles in near-O(1). Key difference from OCaml: 1. **Union
Tutorial
The Problem
Kruskal's algorithm (1956) finds the MST by sorting all edges by weight and adding each edge if it doesn't create a cycle, using a Union-Find (Disjoint Set Union) data structure to detect cycles in near-O(1). While Prim's grows from a vertex, Kruskal's grows from edges. It is more efficient for sparse graphs (E ≈ V) and is the basis for Borůvka's parallel MST algorithm used in distributed computing.
🎯 Learning Outcomes
Code Example
#![allow(clippy::all)]
//! # Kruskal's MST Algorithm
pub struct UnionFind {
parent: Vec<usize>,
rank: Vec<usize>,
}
impl UnionFind {
pub fn new(n: usize) -> Self {
Self {
parent: (0..n).collect(),
rank: vec![0; n],
}
}
pub fn find(&mut self, x: usize) -> usize {
if self.parent[x] != x {
self.parent[x] = self.find(self.parent[x]);
}
self.parent[x]
}
pub fn union(&mut self, x: usize, y: usize) -> bool {
let (px, py) = (self.find(x), self.find(y));
if px == py {
return false;
}
match self.rank[px].cmp(&self.rank[py]) {
std::cmp::Ordering::Less => self.parent[px] = py,
std::cmp::Ordering::Greater => self.parent[py] = px,
std::cmp::Ordering::Equal => {
self.parent[py] = px;
self.rank[px] += 1;
}
}
true
}
}
pub fn kruskals_mst(n: usize, edges: &mut [(usize, usize, i32)]) -> i32 {
edges.sort_by_key(|e| e.2);
let mut uf = UnionFind::new(n);
let mut total = 0;
for &(u, v, w) in edges.iter() {
if uf.union(u, v) {
total += w;
}
}
total
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_kruskals() {
let mut edges = [(0, 1, 10), (0, 2, 6), (0, 3, 5), (1, 3, 15), (2, 3, 4)];
assert_eq!(kruskals_mst(4, &mut edges), 19);
}
}Key Differences
struct UnionFind with methods is idiomatic; OCaml uses a module with mutable arrays, similar in structure.Vec::sort_by_key and OCaml's Array.sort are both O(n log n); the edge comparison is identical.OCaml Approach
OCaml's Union-Find uses Array.make n (Array.init n Fun.id) for the parent array and imperative updates. The path compression is expressed as a recursive function with a ref for the root. OCaml's Array.sort sorts edges in-place. The Ocamlgraph.Kruskal module provides a clean functional implementation using persistent data structures.
Full Source
#![allow(clippy::all)]
//! # Kruskal's MST Algorithm
pub struct UnionFind {
parent: Vec<usize>,
rank: Vec<usize>,
}
impl UnionFind {
pub fn new(n: usize) -> Self {
Self {
parent: (0..n).collect(),
rank: vec![0; n],
}
}
pub fn find(&mut self, x: usize) -> usize {
if self.parent[x] != x {
self.parent[x] = self.find(self.parent[x]);
}
self.parent[x]
}
pub fn union(&mut self, x: usize, y: usize) -> bool {
let (px, py) = (self.find(x), self.find(y));
if px == py {
return false;
}
match self.rank[px].cmp(&self.rank[py]) {
std::cmp::Ordering::Less => self.parent[px] = py,
std::cmp::Ordering::Greater => self.parent[py] = px,
std::cmp::Ordering::Equal => {
self.parent[py] = px;
self.rank[px] += 1;
}
}
true
}
}
pub fn kruskals_mst(n: usize, edges: &mut [(usize, usize, i32)]) -> i32 {
edges.sort_by_key(|e| e.2);
let mut uf = UnionFind::new(n);
let mut total = 0;
for &(u, v, w) in edges.iter() {
if uf.union(u, v) {
total += w;
}
}
total
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_kruskals() {
let mut edges = [(0, 1, 10), (0, 2, 6), (0, 3, 5), (1, 3, 15), (2, 3, 4)];
assert_eq!(kruskals_mst(4, &mut edges), 19);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_kruskals() {
let mut edges = [(0, 1, 10), (0, 2, 6), (0, 3, 5), (1, 3, 15), (2, 3, 4)];
assert_eq!(kruskals_mst(4, &mut edges), 19);
}
}
Deep Comparison
OCaml vs Rust: Kruskals Mst
Overview
See the example.rs and example.ml files for detailed implementations.
Key Differences
| Aspect | OCaml | Rust |
|---|---|---|
| Type system | Hindley-Milner | Ownership + traits |
| Memory | GC | Zero-cost abstractions |
| Mutability | Explicit ref | mut keyword |
| Error handling | Option/Result | Result<T, E> |
See README.md for detailed comparison.
Exercises
Vec<(usize, usize, i32)>, not just the total weight.max_spanning_tree by reversing the edge sort order — identical to Kruskal's but taking maximum-weight edges.