799-bellman-ford — Bellman-Ford Algorithm
Tutorial Video
Text description (accessibility)
This video demonstrates the "799-bellman-ford — Bellman-Ford Algorithm" functional Rust example. Difficulty level: Advanced. Key concepts covered: Functional Programming. Bellman-Ford (1958/1965) finds shortest paths from a source to all vertices in a weighted graph, handling negative edge weights that Dijkstra cannot handle. Key difference from OCaml: 1. **Overflow guard**: Rust uses `i32::MAX` as infinity and guards with `!= i32::MAX` before adding; OCaml uses `max_int/2` to avoid overflow — both serve the same purpose.
Tutorial
The Problem
Bellman-Ford (1958/1965) finds shortest paths from a source to all vertices in a weighted graph, handling negative edge weights that Dijkstra cannot handle. It also detects negative-weight cycles. Used in network routing (RIP — Routing Information Protocol uses Bellman-Ford), currency arbitrage detection, and financial risk analysis. The O(VE) time complexity is slower than Dijkstra's O((V+E)logV) but handles a broader class of problems.
🎯 Learning Outcomes
None for graphs with negative cycles (shortest path is undefined)Code Example
#![allow(clippy::all)]
//! # Bellman-Ford Algorithm
pub fn bellman_ford(n: usize, edges: &[(usize, usize, i32)], src: usize) -> Option<Vec<i32>> {
let mut dist = vec![i32::MAX; n];
dist[src] = 0;
for _ in 0..n - 1 {
for &(u, v, w) in edges {
if dist[u] != i32::MAX && dist[u] + w < dist[v] {
dist[v] = dist[u] + w;
}
}
}
for &(u, v, w) in edges {
if dist[u] != i32::MAX && dist[u] + w < dist[v] {
return None; // Negative cycle
}
}
Some(dist)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bellman() {
let edges = [
(0, 1, -1),
(0, 2, 4),
(1, 2, 3),
(1, 3, 2),
(1, 4, 2),
(3, 2, 5),
(3, 1, 1),
(4, 3, -3),
];
let dist = bellman_ford(5, &edges, 0).unwrap();
// Shortest paths from 0: 0→1=-1, 0→1→4=1, 0→1→4→3=-2
assert_eq!(dist[0], 0);
assert_eq!(dist[1], -1);
assert_eq!(dist[3], -2);
assert_eq!(dist[4], 1);
}
}Key Differences
i32::MAX as infinity and guards with != i32::MAX before adding; OCaml uses max_int/2 to avoid overflow — both serve the same purpose.OCaml Approach
OCaml implements with Array.make n max_int and nested for loops. The negative cycle check adds one more relaxation attempt. Functional style using List.iter over edges is idiomatic. OCaml's Hashtbl can represent the adjacency list. The Int.max_int / 2 sentinel avoids overflow when adding edges to "infinity" values.
Full Source
#![allow(clippy::all)]
//! # Bellman-Ford Algorithm
pub fn bellman_ford(n: usize, edges: &[(usize, usize, i32)], src: usize) -> Option<Vec<i32>> {
let mut dist = vec![i32::MAX; n];
dist[src] = 0;
for _ in 0..n - 1 {
for &(u, v, w) in edges {
if dist[u] != i32::MAX && dist[u] + w < dist[v] {
dist[v] = dist[u] + w;
}
}
}
for &(u, v, w) in edges {
if dist[u] != i32::MAX && dist[u] + w < dist[v] {
return None; // Negative cycle
}
}
Some(dist)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bellman() {
let edges = [
(0, 1, -1),
(0, 2, 4),
(1, 2, 3),
(1, 3, 2),
(1, 4, 2),
(3, 2, 5),
(3, 1, 1),
(4, 3, -3),
];
let dist = bellman_ford(5, &edges, 0).unwrap();
// Shortest paths from 0: 0→1=-1, 0→1→4=1, 0→1→4→3=-2
assert_eq!(dist[0], 0);
assert_eq!(dist[1], -1);
assert_eq!(dist[3], -2);
assert_eq!(dist[4], 1);
}
}#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bellman() {
let edges = [
(0, 1, -1),
(0, 2, 4),
(1, 2, 3),
(1, 3, 2),
(1, 4, 2),
(3, 2, 5),
(3, 1, 1),
(4, 3, -3),
];
let dist = bellman_ford(5, &edges, 0).unwrap();
// Shortest paths from 0: 0→1=-1, 0→1→4=1, 0→1→4→3=-2
assert_eq!(dist[0], 0);
assert_eq!(dist[1], -1);
assert_eq!(dist[3], -2);
assert_eq!(dist[4], 1);
}
}
Deep Comparison
OCaml vs Rust: Bellman Ford
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
bellman_ford_path(n, edges, src, dst) -> Option<Vec<usize>> that reconstructs the shortest path from src to dst using a predecessor array.