algorithms_edu/algo/
graph.rs

1//! # Graph Theory Algorithms
2//!
3//! This module contains implementations of graph and tree representations and algorithms.
4//!
5//! I highly recommend watching [William Fiset's video series on graph theory algorithms](https://www.youtube.com/watch?v=DgXR2OWQnLc&list=PLDV1Zeh2NRsDGO4--qE8yH72HFL1Km93P)
6//! While following along, try implementing the algorithms yourself before comparing them to implementations presented here or
7//! William's original Java implementations.
8
9pub mod bfs;
10pub mod bipartite_check;
11pub mod dfs;
12pub mod eulerian_path;
13pub mod minimum_spanning_tree;
14pub mod network_flow;
15pub mod shortest_path;
16pub mod tarjan_scc;
17pub mod topological_sort;
18pub mod tree;
19
20use std::fmt;
21
22/// The Edge type used in [`WeightedAdjacencyList`].
23/// A `from` field is not required because it's implied by its position in the adjacency list.
24#[derive(Debug, Copy, Clone)]
25pub struct Edge {
26    pub to: usize,
27    pub weight: f64,
28}
29impl Edge {
30    /// Consruct a new (directed) weighted `Edge`
31    pub fn new(to: usize, weight: f64) -> Self {
32        Self { to, weight }
33    }
34}
35
36/// A graph represented by a weighted adjacency list.
37/// Under the hood, a weighted adjacency list is a `Vec` of `Vec` of `Edge`s.
38/// For an adjacency list `g`, `g[i]` is a `Vec` of edges pointing from `i` to other nodes (vertices).
39/// Thus, the number of nodes is implied by the `len` of the (outer) `Vec`.
40/// For each node `i` that do not have outgoing edges, `g[i]` is an empty vector.
41#[derive(Debug)]
42pub struct WeightedAdjacencyList {
43    inner: Vec<Vec<Edge>>,
44}
45
46impl WeightedAdjacencyList {
47    /// Initialize an empty adjacency list that can hold up to n nodes.
48    pub fn with_size(n: usize) -> Self {
49        Self {
50            inner: vec![vec![]; n],
51        }
52    }
53    /// Is the graph devoid of vertices?
54    pub fn is_empty(&self) -> bool {
55        self.inner.is_empty()
56    }
57    /// Add a directed edge from node `u` to node `v` with weight `weight`.
58    pub fn add_directed_edge(&mut self, u: usize, v: usize, weight: f64) {
59        self.inner[u].push(Edge::new(v, weight))
60    }
61    /// Add an undirected edge between nodes `u` and `v`.
62    pub fn add_undirected_edge(&mut self, u: usize, v: usize, weight: f64) {
63        self.add_directed_edge(u, v, weight);
64        self.add_directed_edge(v, u, weight);
65    }
66    pub fn new_directed(size: usize, edges: &[(usize, usize, f64)]) -> Self {
67        let mut graph = Self::with_size(size);
68        for &(a, b, c) in edges.iter() {
69            graph.add_directed_edge(a, b, c);
70        }
71        graph
72    }
73    pub fn new_undirected(size: usize, edges: &[(usize, usize, f64)]) -> Self {
74        let mut graph = Self::with_size(size);
75        for &(a, b, c) in edges.iter() {
76            graph.add_undirected_edge(a, b, c);
77        }
78        graph
79    }
80    pub fn new_directed_unweighted(size: usize, edges: &[[usize; 2]]) -> Self {
81        let mut graph = Self::with_size(size);
82        for &[a, b] in edges.iter() {
83            graph.add_directed_edge(a, b, 1.);
84        }
85        graph
86    }
87    pub fn new_undirected_unweighted(size: usize, edges: &[[usize; 2]]) -> Self {
88        let mut graph = Self::with_size(size);
89        for &[a, b] in edges.iter() {
90            graph.add_undirected_edge(a, b, 1.);
91        }
92        graph
93    }
94    /// Iterates over all edges in the gragh.
95    /// Each item is a tuples of 3: `(from, to, weight)`
96    pub fn edges(&self) -> impl Iterator<Item = (usize, usize, f64)> + '_ {
97        self.inner
98            .iter()
99            .enumerate()
100            .flat_map(|(a, edges)| edges.iter().map(move |b| (a, b.to, b.weight)))
101    }
102    /// Number of edges in the graph
103    pub fn edge_count(&self) -> usize {
104        self.edges().count()
105    }
106    /// Iterates over all nodes in the graph.
107    /// Each item is a tuple of the node id and a `Vec` of all its outgoing edges
108    pub fn nodes(&self) -> impl Iterator<Item = (usize, &Vec<Edge>)> {
109        self.inner.iter().enumerate()
110    }
111    /// Number of nodes (vertices) in the graph
112    pub fn node_count(&self) -> usize {
113        self.inner.len()
114    }
115}
116
117/// Allows the outgoing edges of a node to be accessed easily.
118impl std::ops::Index<usize> for WeightedAdjacencyList {
119    type Output = Vec<Edge>;
120    fn index(&self, index: usize) -> &Self::Output {
121        &self.inner[index]
122    }
123}
124
125/// An unweighted graph represented by an unweighted adjacency list.
126/// This is in principle the same as `WeightedAdjacencyList`, except that no weights are involved
127/// and thus a simple `usize` is able to represent an outgoing edge.
128#[derive(Debug)]
129pub struct UnweightedAdjacencyList {
130    inner: Vec<Vec<usize>>,
131}
132
133impl UnweightedAdjacencyList {
134    /// Initialize an empty adjacency list that can hold up to n nodes.
135    pub fn with_size(n: usize) -> Self {
136        Self {
137            inner: vec![vec![]; n],
138        }
139    }
140    pub fn is_empty(&self) -> bool {
141        self.inner.is_empty()
142    }
143    /// Add a directed edge from node `u` to node `v`
144    pub fn add_directed_edge(&mut self, u: usize, v: usize) {
145        self.inner[u].push(v)
146    }
147    /// Add an undirected edge between nodes `u` and `v`.
148    pub fn add_undirected_edge(&mut self, u: usize, v: usize) {
149        self.add_directed_edge(u, v);
150        self.add_directed_edge(v, u);
151    }
152    pub fn new_directed(size: usize, edges: &[[usize; 2]]) -> Self {
153        let mut graph = Self::with_size(size);
154        for &[a, b] in edges.iter() {
155            graph.add_directed_edge(a, b);
156        }
157        graph
158    }
159    pub fn new_undirected(size: usize, edges: &[[usize; 2]]) -> Self {
160        let mut graph = Self::with_size(size);
161        for &[a, b] in edges.iter() {
162            graph.add_undirected_edge(a, b);
163        }
164        graph
165    }
166    /// Iterates over all edges in the gragh.
167    /// Each item is an array of length 2, showing the source and destination of this edge.
168    pub fn edges(&self) -> impl Iterator<Item = [usize; 2]> + '_ {
169        self.inner
170            .iter()
171            .enumerate()
172            .flat_map(|(a, edges)| edges.iter().map(move |&b| [a, b]))
173    }
174    /// Number of edges in the graph
175    pub fn edge_count(&self) -> usize {
176        self.edges().count()
177    }
178    /// Iterates over all nodes in the graph.
179    /// Each item is a tuple of the node id and a `Vec` of all its outgoing edges
180    pub fn nodes(&self) -> impl Iterator<Item = (usize, &Vec<usize>)> {
181        self.inner.iter().enumerate()
182    }
183    /// Number of nodes (vertices) in the graph
184    pub fn node_count(&self) -> usize {
185        self.inner.len()
186    }
187}
188
189/// Allows the outgoing edges of a node to be accessed easily.
190impl std::ops::Index<usize> for UnweightedAdjacencyList {
191    type Output = Vec<usize>;
192    fn index(&self, index: usize) -> &Self::Output {
193        &self.inner[index]
194    }
195}
196
197/// Dense graphs, are sometimes more efficient to be represented as adjacency matrices.
198/// A `WeightedAdjacencyMatrix` is based on a Matrix of size `n * n` where n is the number of nodes (vertices) in
199/// the graph.
200/// For a `WeightedAdjacencyMatrix` `g`, `g[i][j]` is the weight of the edge pointing from node `i`
201/// to node `j`. By convention, for two nodes `i` and `j` that are *not* connected, `g[i][j] = INFINITY`,
202/// and each node by default has a weight of `0` to point to itself (i.e. `g[i][i]` = 0).
203pub struct WeightedAdjacencyMatrix {
204    inner: Vec<Vec<f64>>,
205}
206
207impl WeightedAdjacencyMatrix {
208    #[allow(clippy::needless_range_loop)]
209    pub fn with_size(n: usize) -> Self {
210        // By default, all vertices are not connected and this is indicated by a weight of `INFINITY` between them
211        let mut inner = vec![vec![f64::INFINITY; n]; n];
212        // distance of each vertex to itself defaults to zero.
213        for i in 0..n {
214            inner[i][i] = 0.;
215        }
216        Self { inner }
217    }
218    /// Number of nodes in the graph.
219    pub fn node_count(&self) -> usize {
220        self.inner.len()
221    }
222    /// Converts a `WeightedAdjacencyList` to `WeightedAdjacencyMatrix`
223    pub fn from_adjacency_list(inp: &WeightedAdjacencyList) -> Self {
224        let mut res = Self::with_size(inp.node_count());
225        for (from, edges) in inp.nodes() {
226            for &Edge { to, weight } in edges {
227                res.inner[from][to] = weight;
228            }
229        }
230        res
231    }
232    /// Builds a `WeightedAdjacencyMatrix` from its underlying representation.
233    pub fn from_inner(matrix: Vec<Vec<f64>>) -> Self {
234        Self { inner: matrix }
235    }
236}
237
238/// This allows us to access the weight of edge `i -> j` in graph `g`
239/// by `g[i][j]` rather than `g.inner[i][j]`
240impl std::ops::Index<usize> for WeightedAdjacencyMatrix {
241    type Output = Vec<f64>;
242    fn index(&self, index: usize) -> &Self::Output {
243        &self.inner[index]
244    }
245}
246
247/// For convinience
248impl From<WeightedAdjacencyList> for WeightedAdjacencyMatrix {
249    fn from(inp: WeightedAdjacencyList) -> Self {
250        Self::from_adjacency_list(&inp)
251    }
252}
253
254/// For convinience
255impl From<Vec<Vec<f64>>> for WeightedAdjacencyMatrix {
256    fn from(matrix: Vec<Vec<f64>>) -> Self {
257        Self::from_inner(matrix)
258    }
259}
260
261/// Pretty-prints a small graph represented by an adjacency matrix
262impl fmt::Display for WeightedAdjacencyMatrix {
263    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264        let n = self.node_count();
265        write!(f, "   ")?;
266        for i in 0..n {
267            write!(f, "{:>2} ", i)?;
268        }
269        writeln!(f)?;
270        for i in 0..n {
271            write!(f, "{:>2} ", i)?;
272            for j in 0..n {
273                let x = self[i][j];
274                if x == f64::INFINITY {
275                    write!(f, " ∞ ")?;
276                } else if x == f64::NEG_INFINITY {
277                    write!(f, "-∞ ")?;
278                } else {
279                    write!(f, "{:>2} ", self[i][j])?;
280                }
281            }
282            writeln!(f)?;
283        }
284        Ok(())
285    }
286}
287
288/// Pretty-prints a small graph represented by a weighted adjacency list
289/// The graph is first converted to a `WeightedAdjacencyMatrix` before being printed
290impl fmt::Display for WeightedAdjacencyList {
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        writeln!(f, "{}", WeightedAdjacencyMatrix::from_adjacency_list(self))
293    }
294}
295
296/// An adjacency matrix representing an undirected graph is symmetric with respect to the main diagonal,
297/// i.e. `g[i][j]` = `g[j][i]`. Thus, only half of the values in the matrix need to be stored. In addition,
298/// The assumption that `g[i][i] = 0` works fine in most cases, so weights representing these self-pointing
299/// edges are also not stored.
300///
301/// Thus a condensed matrix stores only `g[i][j]` where `i < j` in a vector of length ${}^{n}C_{2} = \\dfrac{n(n-1)}{2}$
302/// (n-choose-2). For example, in a graph with 4 vertices, the 6 weights in the condensed vector represents
303/// `g[0 -> 1], g[0 -> 2], g[0 -> 3], g[1 -> 2], g[1 -> 3], g[2 -> 3]`, respectively.
304#[derive(Debug)]
305pub struct WeightedUndirectedAdjacencyMatrixCondensed {
306    inner: Vec<f64>,
307    n: usize,
308}
309
310impl WeightedUndirectedAdjacencyMatrixCondensed {
311    pub fn new(node_count: usize) -> Self {
312        Self {
313            inner: vec![f64::INFINITY; node_count * (node_count - 1) / 2],
314            n: node_count,
315        }
316    }
317    /// Build a `WeightedUndirectedAdjacencyMatrixCondensed` from [`WeightedAdjacencyList`].
318    /// The graph must be undirected. Even if the [`WeightedAdjacencyList`] were build with
319    /// directed edges, they are treated as undirected edges.
320    ///
321    /// # Panics
322    ///
323    /// Panics if the [`WeightedAdjacencyList`] contains both `g[i -> j]` and `g[j -> i]` but
324    /// their weights differ
325    pub fn from_adjacency_list(inp: &WeightedAdjacencyList) -> Self {
326        let n = inp.node_count();
327        let mut m = Self {
328            inner: vec![f64::INFINITY; n * (n - 1) / 2],
329            n,
330        };
331        for (i, j, weight) in inp.edges() {
332            let w = &mut m[(i, j)];
333            if w.is_finite() {
334                assert!(
335                    (*w - weight).abs() < f64::EPSILON,
336                    "Graph contains directed edge(s)!"
337                );
338            } else {
339                *w = weight;
340            }
341        }
342        m
343    }
344    /// Builds a `WeightedUndirectedAdjacencyMatrixCondensed` from its inner representation.
345    pub fn from_slice(inp: &[f64]) -> Self {
346        assert!(!inp.is_empty(), "Inpud cannot be empty.");
347        let mut n = 2;
348        loop {
349            n += 1;
350            let len = n * (n - 1) / 2;
351            if len == inp.len() {
352                return Self {
353                    inner: inp.to_owned(),
354                    n,
355                };
356            }
357            if len > inp.len() {
358                panic!("Invalid input length.")
359            }
360        }
361    }
362    /// Iterate over all pairs of nodes `(i, j)` where `i < j` with the weight associated with the pair.
363    /// Each item is a tuple of 3: `(i, j, weight)`.
364    /// Note that only `(i, j)` but not `(j, i)` is outputted.
365    pub fn edges(&self) -> impl Iterator<Item = (usize, usize, f64)> + '_ {
366        (0..self.n - 1)
367            .flat_map(move |i| (i + 1..self.n).map(move |j| (i, j)))
368            .zip(self.inner.iter())
369            .map(|((i, j), w)| (i, j, *w))
370    }
371    /// Number of nodes (vertices) in the graph
372    pub fn node_count(&self) -> usize {
373        self.n
374    }
375    pub fn resized(&self, new_node_count: usize) -> Self {
376        let mut new = Self::new(new_node_count);
377        for (i, j, weight) in self.edges() {
378            if i < new_node_count && j < new_node_count {
379                new[(i, j)] = weight;
380            }
381        }
382        new
383    }
384}
385
386/// This allows indexing into graphs represented by [`WeightedUndirectedAdjacencyMatrixCondensed`] easier.
387/// For example, for a graph `g`, either `g[(i, j)]` or `g[(j, i)]` will give the weight associated with node
388/// `i` and node `j` (remember the graph is undirected)
389impl std::ops::Index<(usize, usize)> for WeightedUndirectedAdjacencyMatrixCondensed {
390    type Output = f64;
391
392    fn index(&self, (i, j): (usize, usize)) -> &Self::Output {
393        use std::cmp::Ordering::*;
394        assert!(i < self.n && j < self.n, "Index out of bound.");
395        match i.cmp(&j) {
396            Less => {
397                let (mut _i, mut j_, mut j, mut k) = (i, self.n - 1, j - 1, 0);
398                while _i > 0 {
399                    k += j_;
400                    j_ -= 1;
401                    j -= 1;
402                    _i -= 1;
403                }
404                k += j;
405                &self.inner[k]
406            }
407            Greater => self.index((j, i)),
408            Equal => &0.,
409        }
410    }
411}
412impl std::ops::IndexMut<(usize, usize)> for WeightedUndirectedAdjacencyMatrixCondensed {
413    fn index_mut(&mut self, (i, j): (usize, usize)) -> &mut Self::Output {
414        use std::cmp::Ordering::*;
415        match i.cmp(&j) {
416            Less => {
417                let (mut _i, mut j_, mut j, mut k) = (i, self.n - 1, j - 1, 0);
418                while _i > 0 {
419                    k += j_;
420                    j_ -= 1;
421                    j -= 1;
422                    _i -= 1;
423                }
424                k += j;
425                &mut self.inner[k]
426            }
427            Greater => self.index_mut((j, i)),
428            Equal => panic!("Not allowed to assign a weight from a vetex to itself!"),
429        }
430    }
431}
432
433impl From<WeightedAdjacencyList> for WeightedUndirectedAdjacencyMatrixCondensed {
434    fn from(inp: WeightedAdjacencyList) -> Self {
435        Self::from_adjacency_list(&inp)
436    }
437}
438
439impl fmt::Display for WeightedUndirectedAdjacencyMatrixCondensed {
440    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
441        let n = self.node_count();
442        write!(f, "    ")?;
443        for i in 1..n {
444            write!(f, "{:>5} ", i)?;
445        }
446        writeln!(f)?;
447        for i in 0..n - 1 {
448            write!(f, "{:>2} ", i)?;
449            for _ in 0..i {
450                write!(f, "      ")?;
451            }
452            for j in i + 1..n {
453                let x = self[(i, j)];
454                if x == f64::INFINITY {
455                    write!(f, "  ∞  ")?;
456                } else if x == f64::NEG_INFINITY {
457                    write!(f, "  -∞ ")?;
458                } else {
459                    write!(f, " {:>4.2}", x)?;
460                }
461            }
462            writeln!(f)?;
463        }
464        Ok(())
465    }
466}
467
468#[cfg(test)]
469mod tests {
470
471    use super::*;
472    #[test]
473    fn test_graph_adj_list() {
474        let mut edges = vec![[0, 1], [1, 2], [0, 2], [1, 1]];
475        let g = UnweightedAdjacencyList::new_directed(3, &edges);
476        for edge in g.edges() {
477            let i = edges.iter().position(|e| *e == edge).unwrap();
478            edges.remove(i);
479        }
480        assert!(edges.is_empty());
481    }
482
483    #[test]
484    fn test_weighted_undirected_condensed() {
485        let edges = &[
486            (0, 1, 1.),
487            (0, 2, 2.),
488            (0, 3, 3.),
489            (1, 2, 4.),
490            (1, 3, 5.),
491            (2, 3, 6.),
492        ];
493        let g = WeightedAdjacencyList::new_undirected(4, edges);
494        let m = WeightedAdjacencyMatrix::from_adjacency_list(&g);
495        let g1 = WeightedUndirectedAdjacencyMatrixCondensed::from_adjacency_list(&g);
496        let g2 = WeightedUndirectedAdjacencyMatrixCondensed::from_slice(&[1., 2., 3., 4., 5., 6.]);
497        for i in 0..3 {
498            for j in 0..3 {
499                assert_eq!(m[i][j], g1[(i, j)]);
500                assert_eq!(m[i][j], g2[(i, j)]);
501            }
502        }
503        assert_eq!(&g1.edges().collect::<Vec<_>>(), edges);
504    }
505}