806-articulation-points — Articulation Points
Tutorial Video
Text description (accessibility)
This video demonstrates the "806-articulation-points — Articulation Points" functional Rust example. Difficulty level: Advanced. Key concepts covered: Functional Programming. An articulation point (cut vertex) is a vertex whose removal disconnects the graph. Key difference from OCaml: 1. **Two conditions**: The root condition (children > 1) and non
Tutorial
The Problem
An articulation point (cut vertex) is a vertex whose removal disconnects the graph. Finding articulation points in O(V+E) is critical for network reliability analysis: which routers, if removed, would split the internet? Which protein in a protein-protein interaction network is essential? Which road intersection, if closed, disconnects a city? The algorithm also finds bridges (edges whose removal disconnects the graph).
🎯 Learning Outcomes
low[v] value: minimum discovery time reachable from v's subtree via back edgeslow[v] > disc[u]Code Example
#![allow(clippy::all)]
//! # Articulation Points
pub fn articulation_points(n: usize, edges: &[(usize, usize)]) -> Vec<usize> {
let mut adj = vec![vec![]; n];
for &(u, v) in edges {
adj[u].push(v);
adj[v].push(u);
}
let mut disc = vec![0; n];
let mut low = vec![0; n];
let mut parent = vec![None; n];
let mut ap = vec![false; n];
let mut time = 0;
let mut visited = vec![false; n];
fn dfs(
u: usize,
adj: &[Vec<usize>],
disc: &mut [usize],
low: &mut [usize],
parent: &mut [Option<usize>],
ap: &mut [bool],
time: &mut usize,
vis: &mut [bool],
) {
vis[u] = true;
disc[u] = *time;
low[u] = *time;
*time += 1;
let mut children = 0;
for &v in &adj[u] {
if !vis[v] {
children += 1;
parent[v] = Some(u);
dfs(v, adj, disc, low, parent, ap, time, vis);
low[u] = low[u].min(low[v]);
if parent[u].is_none() && children > 1 {
ap[u] = true;
}
if parent[u].is_some() && low[v] >= disc[u] {
ap[u] = true;
}
} else if parent[u] != Some(v) {
low[u] = low[u].min(disc[v]);
}
}
}
for v in 0..n {
if !visited[v] {
dfs(
v,
&adj,
&mut disc,
&mut low,
&mut parent,
&mut ap,
&mut time,
&mut visited,
);
}
}
(0..n).filter(|&i| ap[i]).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ap() {
let ap = articulation_points(5, &[(0, 1), (1, 2), (2, 0), (1, 3), (3, 4)]);
assert!(!ap.is_empty());
}
}Key Differences
parent array to avoid treating the tree edge back to parent as a back edge; the -1 / None sentinel serves the same purpose.traceroute data combined with this algorithm maps internet topology.OCaml Approach
OCaml implements with ref cells for time and mutable arrays for disc, low, parent, ap. Recursive DFS uses let rec dfs u = ... List.iter (fun v -> ...) adj.(u). The Ocamlgraph.Components.articulation_points function provides a ready-made implementation. Bridge finding changes the condition from >= to >.
Full Source
#![allow(clippy::all)]
//! # Articulation Points
pub fn articulation_points(n: usize, edges: &[(usize, usize)]) -> Vec<usize> {
let mut adj = vec![vec![]; n];
for &(u, v) in edges {
adj[u].push(v);
adj[v].push(u);
}
let mut disc = vec![0; n];
let mut low = vec![0; n];
let mut parent = vec![None; n];
let mut ap = vec![false; n];
let mut time = 0;
let mut visited = vec![false; n];
fn dfs(
u: usize,
adj: &[Vec<usize>],
disc: &mut [usize],
low: &mut [usize],
parent: &mut [Option<usize>],
ap: &mut [bool],
time: &mut usize,
vis: &mut [bool],
) {
vis[u] = true;
disc[u] = *time;
low[u] = *time;
*time += 1;
let mut children = 0;
for &v in &adj[u] {
if !vis[v] {
children += 1;
parent[v] = Some(u);
dfs(v, adj, disc, low, parent, ap, time, vis);
low[u] = low[u].min(low[v]);
if parent[u].is_none() && children > 1 {
ap[u] = true;
}
if parent[u].is_some() && low[v] >= disc[u] {
ap[u] = true;
}
} else if parent[u] != Some(v) {
low[u] = low[u].min(disc[v]);
}
}
}
for v in 0..n {
if !visited[v] {
dfs(
v,
&adj,
&mut disc,
&mut low,
&mut parent,
&mut ap,
&mut time,
&mut visited,
);
}
}
(0..n).filter(|&i| ap[i]).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ap() {
let ap = articulation_points(5, &[(0, 1), (1, 2), (2, 0), (1, 3), (3, 4)]);
assert!(!ap.is_empty());
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ap() {
let ap = articulation_points(5, &[(0, 1), (1, 2), (2, 0), (1, 3), (3, 4)]);
assert!(!ap.is_empty());
}
}
Deep Comparison
OCaml vs Rust: Articulation Points
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
find_bridges(n, edges) -> Vec<(usize, usize)> using the same DFS with the modified condition low[v] > disc[u].