rs_graph/mst/
prim.rs

1// Copyright (c) 2016-2022 Frank Fischer <frank-fischer@shadow-soft.de>
2//
3// This program is free software: you can redistribute it and/or
4// modify it under the terms of the GNU General Public License as
5// published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful, but
9// WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11// General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with this program.  If not, see  <http://www.gnu.org/licenses/>
15//
16
17//! Implementation of Kruskal's algorithm
18
19use crate::traits::IndexGraph;
20
21use std::collections::HashMap;
22use std::hash::Hash;
23
24use crate::adjacencies::Neighbors;
25use crate::collections::BinHeap;
26use crate::num::traits::NumAssign;
27use crate::search::astar::Accumulator;
28use crate::shortestpath::dijkstra;
29
30/// Run Prim's algorithm to solve the *Minimum Spanning Tree*
31/// problem on a graph.
32///
33/// * `g` is the undirected graph `weights` the edge weights
34///
35/// If the graph is not connected, the returned vector only spans one
36/// component. This can easily be verifyed by checking the size of the
37/// returned vector.
38///
39/// # Example
40///
41/// ```
42/// use rs_graph::{Net, traits::*};
43/// use rs_graph::mst::prim;
44/// use rs_graph::string::{Data, from_ascii};
45///
46/// let Data { graph, weights, nodes } = from_ascii::<Net>(r"
47///            ------9-----
48///           /            \
49///       ---a---9--   --2--b
50///      /   |\     \ /     |\
51///     4    5 \     c      4 6
52///    /     |  -7-  |\     |  \
53///   d---1--e-    \ 8 --2--f-3-g
54///    \     | \    \|     /|   |
55///     4    |  -9---h---9- |   |
56///      \   3      / \     9  /
57///       \  | -10--   -8-- | 9
58///        \ |/            \|/
59///         -i------18------j
60///     ").unwrap();
61/// let a = nodes[&'a'];
62/// let b = nodes[&'b'];
63/// let c = nodes[&'c'];
64/// let d = nodes[&'d'];
65/// let e = nodes[&'e'];
66/// let f = nodes[&'f'];
67/// let g = nodes[&'g'];
68/// let h = nodes[&'h'];
69/// let i = nodes[&'i'];
70/// let j = nodes[&'j'];
71///
72/// // run the algorithm
73/// let tree = prim(&graph, |e| weights[graph.edge_id(e)]);
74///
75/// // check the results
76/// let mut sum = 0;
77/// for &e in &tree { sum += weights[graph.edge_id(e)]; }
78/// assert_eq!(sum, 38);
79///
80/// let mut tree = tree.into_iter()
81///      .map(|e| graph.enodes(e))
82///      .map(|(u,v)| (graph.node_id(u), graph.node_id(v)))
83///      .map(|(u,v)| if u > v { (v,u) } else { (u,v) })
84///      .collect::<Vec<_>>();
85/// tree.sort();
86///
87/// assert_eq!(tree, vec![(a,d), (a,h), (b,c), (c,f), (c,h), (d,e), (e,i), (f,g), (h,j)]);
88/// ```
89pub fn prim<'a, G, W, F>(g: &'a G, weights: F) -> Vec<G::Edge<'a>>
90where
91    G: IndexGraph,
92    G::Node<'a>: Hash,
93    W: NumAssign + Ord + Copy + 'a,
94    F: Fn(G::Edge<'a>) -> W,
95{
96    if g.num_nodes() == 0 {
97        return vec![];
98    }
99
100    struct WeightAccumulator;
101    impl<T> Accumulator<T> for WeightAccumulator {
102        fn accum(_dist: T, weight: T) -> T {
103            weight
104        }
105    }
106
107    let mut tree = Vec::with_capacity(g.num_nodes() - 1);
108    for (_, e, _) in dijkstra::start_generic::<_, _, _, _, _, WeightAccumulator>(
109        Neighbors(g),
110        g.id2node(0),
111        weights,
112        (HashMap::new(), BinHeap::<_, _, usize>::default()),
113    ) {
114        tree.push(e);
115    }
116    tree
117}