1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Copyright (c) 2016, 2017, 2018, 2020 Frank Fischer <frank-fischer@shadow-soft.de>
//
// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see  <http://www.gnu.org/licenses/>
//

//! Implementation of Worst-Out-Greedy for the Minium-Spanning-Tree problem

use crate::traits::IndexGraph;
use crate::vec::{EdgeVec, NodeVec};

/// Run Worst-Out-Greedy algorithm to solve the *Minimum Spanning Tree*
/// problem on a graph.
///
/// * `g` is the undirected graph `weights` the edge weights
///
/// The algorithm actually solved a minimum spanning *forest* problem
/// in the graph. This can easily be verified by checking the size of
/// the returned vector.
///
/// # Example
///
/// ```
/// use rs_graph::{Net, traits::*, EdgeVec};
/// use rs_graph::mst::worstout;
/// use rs_graph::string::{Data, from_ascii};
///
/// let Data { graph, weights, nodes } = from_ascii::<Net>(r"
///            ------9-----
///           /            \
///       ---a---9--   --2--b
///      /   |\     \ /     |\
///     4    5 \     c      4 6
///    /     |  -7-  |\     |  \
///   d---1--e-    \ 8 --2--f-3-g
///    \     | \    \|     /|   |
///     4    |  -9---h---9- |   |
///      \   3      / \     9  /
///       \  | -10--   -8-- | 9
///        \ |/            \|/
///         -i------18------j
///     ").unwrap();
/// let a = nodes[&'a'];
/// let b = nodes[&'b'];
/// let c = nodes[&'c'];
/// let d = nodes[&'d'];
/// let e = nodes[&'e'];
/// let f = nodes[&'f'];
/// let g = nodes[&'g'];
/// let h = nodes[&'h'];
/// let i = nodes[&'i'];
/// let j = nodes[&'j'];
///
/// // run the algorithm
/// let weights = EdgeVec::new_from_vec(&graph, weights);
/// let tree = worstout(&graph, |e| weights[e]);
///
/// // check the results
/// let mut sum = 0;
/// for &e in &tree { sum += weights[e]; }
/// assert_eq!(sum, 38);
///
/// let mut tree = tree.into_iter()
///      .map(|e| graph.enodes(e))
///      .map(|(u,v)| if graph.node_id(u) > graph.node_id(v) { (v,u) } else { (u,v) })
///      .collect::<Vec<_>>();
/// tree.sort_by_key(|&(u,v)| (graph.node_id(u), graph.node_id(v)));
///
/// assert_eq!(tree, vec![(a,d), (a,h), (b,c), (c,f), (c,h), (d,e), (e,i), (f,g), (h,j)]);
/// ```
pub fn worstout<'a, 'b, G, W, F>(g: &'a G, weights: F) -> Vec<G::Edge>
where
    G: IndexGraph<'a>,
    W: Copy + Ord,
    F: Fn(G::Edge) -> W,
{
    let mut used = EdgeVec::new(g, true);
    let mut edges: Vec<_> = g.edges().collect();
    edges.sort_by_key(|&e| weights(e));

    let mut stack = Vec::with_capacity(g.num_nodes());
    let mut seen = NodeVec::new(g, false);
    for e in edges.into_iter().rev() {
        let (u, v) = g.enodes(e);

        // dfs from u to v
        seen.reset(false);
        seen[u] = true;
        stack.clear();
        stack.push(u);

        // mark this edge as not used, so dfs ignores it
        used[e] = false;
        let mut found = false;
        while let Some(x) = stack.pop() {
            for (_, y) in g.neighs(x).filter(|&(f, _)| used[f]) {
                if !seen[y] {
                    if y == v {
                        found = true;
                        break;
                    }
                    seen[y] = true;
                    stack.push(y);
                }
            }
        }

        // if we have not found another path, keep e
        used[e] = !found;
    }

    g.edges().filter(|&e| used[e]).collect()
}