rs_graph/mst/kruskal.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
21/// Run Kruskal's algorithm to solve the *Minimum Spanning Tree*
22/// problem on a graph.
23///
24/// * `g` is the undirected graph `weights` the edge weights
25///
26/// The algorithm actually solves a minimum spanning *forest* problem
27/// if the graph is not connected. This can easily be verified by
28/// checking the number of returned edges.
29///
30/// # Example
31///
32/// ```
33/// use rs_graph::{Net, traits::*};
34/// use rs_graph::mst::kruskal;
35/// use rs_graph::string::{Data, from_ascii};
36///
37/// let Data { graph, weights, nodes } = from_ascii::<Net>(r"
38/// ------9-----
39/// / \
40/// ---a---9-- --2--b
41/// / |\ \ / |\
42/// 4 5 \ c 4 6
43/// / | -7- |\ | \
44/// d---1--e- \ 8 --2--f-3-g
45/// \ | \ \| /| |
46/// 4 | -9---h---9- | |
47/// \ 3 / \ 9 /
48/// \ | -10-- -8-- | 9
49/// \ |/ \|/
50/// -i------18------j
51/// ").unwrap();
52/// let a = nodes[&'a'];
53/// let b = nodes[&'b'];
54/// let c = nodes[&'c'];
55/// let d = nodes[&'d'];
56/// let e = nodes[&'e'];
57/// let f = nodes[&'f'];
58/// let g = nodes[&'g'];
59/// let h = nodes[&'h'];
60/// let i = nodes[&'i'];
61/// let j = nodes[&'j'];
62///
63/// // run the algorithm
64/// let tree = kruskal(&graph, |e| weights[graph.edge_id(e)]);
65///
66/// // check the results
67/// let mut sum = 0;
68/// for &e in &tree { sum += weights[graph.edge_id(e)]; }
69/// assert_eq!(sum, 38);
70///
71/// let mut tree = tree.into_iter()
72/// .map(|e| graph.enodes(e))
73/// .map(|(u,v)| (graph.node_id(u), graph.node_id(v)))
74/// .map(|(u,v)| if u > v { (v,u) } else { (u,v) })
75/// .collect::<Vec<_>>();
76/// tree.sort();
77///
78/// assert_eq!(tree, vec![(a,d), (a,h), (b,c), (c,f), (c,h), (d,e), (e,i), (f,g), (h,j)]);
79/// ```
80pub fn kruskal<'a, 'b, G, W, F>(g: &'a G, weights: F) -> Vec<G::Edge<'a>>
81where
82 G: IndexGraph,
83 W: Ord,
84 F: Fn(G::Edge<'a>) -> W,
85{
86 let mut edges: Vec<_> = g.edges().collect();
87 edges.sort_by_key(|&e| weights(e));
88
89 // parent map for finding
90 let mut comps = vec![Component::Root(0); g.num_nodes()];
91 let mut tree = Vec::with_capacity(g.num_nodes() - 1);
92
93 for e in edges {
94 let (u, v) = g.enodes(e);
95 let u = g.node_id(u);
96 let v = g.node_id(v);
97 let (uroot, udepth) = find_root(&comps, u);
98 let (vroot, vdepth) = find_root(&comps, v);
99 if uroot != vroot {
100 tree.push(e);
101 if g.num_nodes() - 1 == tree.len() {
102 break;
103 }
104 if udepth < vdepth {
105 comps[uroot] = Component::Node(vroot);
106 } else {
107 comps[vroot] = Component::Node(uroot);
108 if udepth == vdepth {
109 comps[uroot] = Component::Root(udepth + 1);
110 }
111 }
112 }
113 }
114
115 tree
116}
117
118/// Union-Find data-structure for Kruskal.
119#[derive(Clone, Copy)]
120enum Component {
121 /// The root element with the tree's depth.
122 Root(usize),
123 /// An inner node with the parent node.
124 Node(usize),
125}
126
127/// Return the root node and the tree's depth of node `u`.
128fn find_root(comps: &[Component], u: usize) -> (usize, usize) {
129 let mut v = u;
130 loop {
131 match comps[v] {
132 Component::Node(parent) => v = parent,
133 Component::Root(depth) => return (v, depth),
134 }
135 }
136}