Boost depth first visitor minimum spanning tree with graph weights

北城余情 提交于 2019-12-22 11:33:52

问题


I want to create a minimum spanning tree from vertices with edge weights and traverse the graph in depth-first order. I can build the graph and the minimum spanning tree but I am failing at writing the custom visitor.

#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/graph_traits.hpp>

#include <vector>
#include <string>

typedef boost::property<boost::edge_weight_t, double> EdgeWeightProperty;
typedef boost::adjacency_list <
    boost::listS,
    boost::vecS,
    boost::undirectedS,
    boost::no_property,
    EdgeWeightProperty> MyGraph;

typedef MyGraph::edge_descriptor Edge;

class MyVisitor : public boost::default_dfs_visitor
{
    public:
    void tree_edge(Edge e, const MyGraph& g) const {

    }
};

void mst() {
    MyGraph g;
    boost::add_edge(0, 1, 0.7, g);
    boost::add_edge(0, 2, 0.1, g);

    boost::add_edge(1, 2, 0.3, g);
    boost::add_edge(1, 0, 0.7, g);
    boost::add_edge(1, 3, 0.8, g);
    boost::add_edge(1, 4, 0.2, g);

    boost::add_edge(2, 1, 0.3, g);
    boost::add_edge(2, 0, 0.1, g);
    boost::add_edge(2, 5, 0.1, g);
    boost::add_edge(2, 4, 0.5, g);

    boost::add_edge(3, 1, 0.8, g);

    boost::add_edge(4, 1, 0.2, g);
    boost::add_edge(4, 2, 0.5, g);

    boost::add_edge(5, 2, 0.1, g);

    std::list <Edge> spanning_tree;
    boost::kruskal_minimum_spanning_tree(g, std::back_inserter(spanning_tree));

    // the following two lines are failing
    MyVisitor vis();
    boost::depth_first_search(spanning_tree, visitor(vis));
}

int main(int argc, char** argv)
{
    mst();
    std::cin.get();
    return (0);
}

I would like to access the vertices and edge weights in the custom visitor. Is this possible? I saw this post: boost minimum spanning tree, how to do depth first? but I would prefer to not build a separate weight map.

Additionally, is it possible to iterate in depth-first order through the tree with boost tools without writing a custom visitor?


回答1:


MyVisitor vis();

That is a function declaration. See Most Vexing Parse

boost::depth_first_search(spanning_tree, visitor(vis));

That calls the graph algorithm on a std::list<Edge>. depth_first_search requires a graph that models the right graph concepts:

The std::list models neither.

Suggestion

You could build a graph including just the edges from the MST set. The answer to the question you linked to tries that.

However, it seems easier and more efficient to create a filtered_graph<> view of the same graph, so that the edge properties are simply available through the same mechanism.

First, let's prefer to get the MST edges in a set<> instead of a list<>:

struct InSpanning {
    std::set<Edge> edges;
    bool operator()(Edge e) const { return edges.count(e); }
} spanning;

boost::kruskal_minimum_spanning_tree(g, std::inserter(spanning.edges, spanning.edges.end()));

The interesting thing you'll note is that InSpanning is also a function object that be used as a filtering predicate for filtering_graph:

boost::filtered_graph<MyGraph, InSpanning, boost::keep_all> mst(g, spanning, {});

Now you can call de DFS:

boost::depth_first_search(mst, visitor(vis));

I've tweaked the visitor slightly:

struct MyVisitor : boost::default_dfs_visitor {
    template <typename Graph>
    void tree_edge(Edge e, const Graph& g) {
        std::cout << "Visiting: " << e << " with weight " << get(boost::edge_weight, g, e) << "\n";
    }
};

Note:

  1. It doesn't hardcode the MyGraph type anymore (because the filtered_graph has a different type).
  2. It prints the information you wanted to see.

Live Demo

Live On Coliru

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <iostream>

#include <string>
#include <vector>

typedef boost::property<boost::edge_weight_t, double> EdgeWeightProperty;
typedef boost::adjacency_list<boost::listS, boost::vecS, boost::undirectedS, boost::no_property, EdgeWeightProperty>
    MyGraph;

typedef MyGraph::edge_descriptor Edge;

struct MyVisitor : boost::default_dfs_visitor {
    template <typename Graph>
    void tree_edge(Edge e, const Graph& g) {
        std::cout << "Visiting: " << e << " with weight " << get(boost::edge_weight, g, e) << "\n";
    }
};

void run_mst_test() {
    MyGraph g;
    boost::add_edge(0, 1, 0.7, g);
    boost::add_edge(0, 2, 0.1, g);

    boost::add_edge(1, 2, 0.3, g);
    boost::add_edge(1, 0, 0.7, g);
    boost::add_edge(1, 3, 0.8, g);
    boost::add_edge(1, 4, 0.2, g);

    boost::add_edge(2, 1, 0.3, g);
    boost::add_edge(2, 0, 0.1, g);
    boost::add_edge(2, 5, 0.1, g);
    boost::add_edge(2, 4, 0.5, g);

    boost::add_edge(3, 1, 0.8, g);

    boost::add_edge(4, 1, 0.2, g);
    boost::add_edge(4, 2, 0.5, g);

    boost::add_edge(5, 2, 0.1, g);

    struct InSpanning {
        std::set<Edge> edges;
        bool operator()(Edge e) const { return edges.count(e); }
    } spanning;

    boost::kruskal_minimum_spanning_tree(g, std::inserter(spanning.edges, spanning.edges.end()));

    MyVisitor vis;
    boost::filtered_graph<MyGraph, InSpanning, boost::keep_all> mst(g, spanning, {});
    boost::depth_first_search(mst, visitor(vis));
}

int main() {
    run_mst_test();
}

Prints

Visiting: (0,2) with weight 0.1
Visiting: (2,1) with weight 0.3
Visiting: (1,3) with weight 0.8
Visiting: (1,4) with weight 0.2
Visiting: (2,5) with weight 0.1


来源:https://stackoverflow.com/questions/49428082/boost-depth-first-visitor-minimum-spanning-tree-with-graph-weights

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!