在现有数据结构(作为矢量<object * =“”>的边和顶点)上使用BGL算法需要做什么?

时间:2019-05-17 21:10:47

标签: c++ boost graph boost-graph

我有这样的自定义数据结构:

vector<myVertex *> my_vertices;
vector<myEdge *> my_edges;

我的类myEdge具有source()和target()方法,返回myVertex *,所以它应该已经准备好了,对吧?

要在容器中使用BGL图,我需要做什么外部适应?我知道the adaptor examples in the doc,但是有些帮助将不胜感激!

我感兴趣的是纯粹的adjacency_list基本图类型,还不确定我需要的图遍历概念。

到目前为止,我对adjacency_list参数的了解:

adjacency_list<OutEdgeListS, VertexListS, DirectedS,
             VertexProperty, EdgeProperty, GraphProperty, EdgeListS>
  • OutEdgeListSVertexListS是容器的选择器,用于表示(1)每个顶点的边列表和(2)顶点列表的容器。这些容器分别保存为元素vertex_descriptoredge_descriptor。我的容器类型是简单的std :: vector,所以我想我不需要像example / container_gen.cpp中那样创建新的容器类型。一世 必须简单地(可能使用graph_traits)精确地确定容器元素的类型是指向对象的指针。
  • VertexPropertyEdgeProperty旨在用作内部大容量存储,以获取其他信息,例如颜色标签,边缘权重...并自几年以来提供捆绑属性功能。 / li>

我希望顶点和边缘描述符不默认为整数,而是指向我的对象的指针。 BGL文档明确声明这是可行的in the 2002-version of the book,12.1.2:

  

面向对象的图形实现可能使用指针进行堆   分配的顶点对象。使用图特征类,这些   差异由顶点描述符关联类型隐藏。

尽管它似乎已从当前的1.70在线文档中消失。

理想情况下,我想这样初始化:

MyGraph g(const& my_edges,const& my_vertices,
  undirected_tag, some_color, someweights, allow_parallel_edges_tag);

P.S。我对在property_map中填充对象指针不感兴趣。我不愿意使用'default vecS',这是一个std :: vector,其中描述符是一个整数。 我愿意使用“自定义vecS”作为对象指针的std :: vector;对于OutEdgeList和VertexList。

编辑:这与“ 1”是完全相同的问题。 this one中的。除非它从未得到答案...而且建议的解决方案是针对“ 2.”,以及property_map和昂贵的双重映射:)。在研究了数百个SO主题数小时之后,大多数人似乎建议使用property_maps而不是使用自定义容器。人们倾向于使用property_maps存储实际的节点和边线-它们的对象指针,并让vertex&edge_descriptor保留纯粹的默认整数索引。然而,from what I read here的内部存在一个“下面”的vertex_descriptor或一个实数索引层以增强。

作为上下文:我计划使用dijkstra / johnson_all_pairs_shortest_paths(具有前身地图和访客吗?),并进一步使用带有http://paal.mimuw.edu.pl/(位于bgl之上的库)的斯坦纳树的best-dreyfus-wagner。 为dbms-erd工具pgmodeler https://github.com/pgmodeler/pgmodeler/pull/1232制作sql join-solver。

20/05/19:回复sehe的回答

很棒的信息,它们将所有信息粘合在一起,使我掌握了诸如图形概念之类的一些核心点。 我问我如何在自定义数据结构中使用邻接表,然后您去解释了如何定义一个完全自定义的图。

我将要研究方法之间的权衡:

  1. 保持我的数据结构不变,并保留自定义图形的解决方案。一世 将花费大量时间甚至没有时间进行初始化,但是可能很多 更多时间寻找前沿。空间复杂度低,但时间长 复杂性。
  2. 相同的方法,但是重构我的库,添加专用存储, 每个顶点的入射边向量(作为的类属性 myVertex?)。恒定时间的边缘查找,而不是O(log(n))与 (1)std :: equal_range吗?也许是最好的选择。
  3. 使用adjacency_list但具有bgl时间复杂性 保证。
    • 实例化默认邻接列表,使用我的 库容器/使用捆绑/内部属性。高空间 复杂性;时间复杂度低,但仅适用于bgl算法 初始化会很长。
    • 您是否还要详细说明是否拥有适当的OutEdgeList和 VertexList通过自定义使用邻接列表类 容器选项?持续引用那些持续时间吗?我猜测 在这一点上,adjacency_list的实现可能不是 如此灵活。

1 个答案:

答案 0 :(得分:2)

有关Graph概念的文档位于此处方便:https://www.boost.org/doc/libs/1_70_0/libs/graph/doc/graph_concepts.html

所以-您从未告诉我们您打算使用哪种算法。

所以让我举个例子:BFS。 The docs说,这要求:

  

有向图或无向图。图形类型必须是Vertex List GraphIncidence Graph的模型。

查看您先前存在的数据结构,看来您仅轻松涵盖了“顶点列表”用例。

更多地将边缘实现为边缘列表。没有运行时或存储开销(这是数学的,与库或代码质量无关),就无法从边缘列表中模拟事件图。

  

实际上,您很可能会忽略了与该问题相关的现有数据结构的一部分,因为大多数算法在“顶点+边缘”列表上都将是次优的。

     

实际上,我想您的Edge列表可能像经典的邻接表一样进行组织(例如,按源顶点排序,因此您可以按源顶点进行O(log(n))查找)。

     

对于下面的示例,我假设是这种情况。请记住,我们只是通过事件图概念来接近保证复杂性:

     
    

复杂性保证

         

source()target()out_edges()函数必须均为恒定时间。 out_degree()函数的边缘数量必须是线性的。

  
     

要真正满足这些要求,您将需要为每个顶点专门存储边缘(out-edges)

所以,我们去吧:

模拟您的图书馆

namespace YourLibrary {
    struct myVertex {
    };

    struct myEdge {
        myVertex* _s = nullptr;
        myVertex* _t = nullptr;

        myVertex* source() const { return _s; }
        myVertex* target() const { return _t; }
    };

    using Vertices = std::vector<myVertex *>;
    using Edges = std::vector<myEdge *>;
}

实现图形概念

您想保留对现有数据结构的引用:

namespace Glue {

    struct MyGraph {
        struct EdgeOrder {
            template <typename A, typename B>
                bool operator()(A const* a, B const* b) const { return source(a) < source(b); }
            private:
            static auto source(YourLibrary::myVertex const* v) { return v; }
            static auto source(YourLibrary::myEdge const* e) { return e->source(); }
        };

        using Vertices = YourLibrary::Vertices;
        using Edges = YourLibrary::Edges;

        Vertices& _vertices;
        Edges& _edges;

        MyGraph(Vertices& vv, Edges& ee) : _vertices(vv), _edges(ee)  { }
    };
}

现在,我将逐个列出每个概念所需的特征类型列表:

namespace boost {

    template <> struct graph_traits<Glue::MyGraph> {
        // Due to Graph concept
        using vertex_descriptor      = YourLibrary::myVertex*;
        using edge_descriptor        = YourLibrary::myEdge*;
        using directed_category      = directed_tag;
        using edge_parallel_category = allow_parallel_edge_tag;
        static vertex_descriptor null_vertex() { return nullptr; }

        // Due to Vertex List concept
        struct traversal_category : vertex_list_graph_tag, incidence_graph_tag { };
        using vertex_iterator        = Glue::MyGraph::Vertices::const_iterator;
        using vertices_size_type     = std::size_t;

        // Due to Incidence Graph concept
        using out_edge_iterator = Glue::MyGraph::Edges::const_iterator;
        using degree_size_type = std::size_t;
    };

}

最后重新打开名称空间,以便ADL可以找到满足“有效表达式”条件所需的以下功能:

namespace Glue {
    // Due to Vertex List concept
    auto vertices(MyGraph const& g) {
        return std::make_pair(g._vertices.begin(), g._vertices.end());
    }

    std::size_t num_vertices(MyGraph const& g) {
        return g._vertices.size();
    }

    // Due to Incidence Graph concept
    auto source(YourLibrary::myEdge const* e, MyGraph const& g) {
        return e->source();
    }
    auto target(YourLibrary::myEdge const* e, MyGraph const& g) {
        return e->target();
    }

    auto out_edges(YourLibrary::myVertex const* v, MyGraph const& g) {
        return std::equal_range(g._edges.begin(), g._edges.end(), v, MyGraph::EdgeOrder{});;
    }
    std::size_t out_degree(YourLibrary::myVertex const* v, MyGraph const& g) {
        auto oee = std::equal_range(g._edges.begin(), g._edges.end(), v, MyGraph::EdgeOrder{});
        return std::distance(oee.first, oee.second);
    }
}

这大致上 功能 等同于顶点容器带有setS的邻接列表。

查看 Live On Coliru

运行BFS

此外,所有这些都是算法的参数。您既需要颜色图又需要顶点索引图。这是完全正常的,如果您有例如adjacency_list<vecS, listS, directedS>

我将在MyGraph包装器中隐藏索引图,并公开颜色图,以便您选择首选项:

Live On Coliru

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/breadth_first_search.hpp>
#include <boost/container/flat_map.hpp>
#include <algorithm>

namespace YourLibrary {
    struct myVertex {
    };

    struct myEdge {
        myVertex* _s = nullptr;
        myVertex* _t = nullptr;

        myVertex* source() const { return _s; }
        myVertex* target() const { return _t; }
    };

    using Vertices = std::vector<myVertex *>;
    using Edges = std::vector<myEdge *>;
}

namespace Glue {

    struct MyGraph {
        struct EdgeOrder {
            template <typename A, typename B>
                bool operator()(A const* a, B const* b) const { return source(a) < source(b); }
            private:
            static auto source(YourLibrary::myVertex const* v) { return v; }
            static auto source(YourLibrary::myEdge const* e) { return e->source(); }
        };

        using Vertices = YourLibrary::Vertices;
        using Edges = YourLibrary::Edges;

        using Index = boost::container::flat_map<Vertices::value_type, std::size_t>;

        Vertices& _vertices;
        Edges& _edges;
        Index _index;

        MyGraph(Vertices& vv, Edges& ee) : _vertices(vv), _edges(ee)  {
            _index.reserve(vv.size());
            std::size_t i = 0;
            for(auto v : vv) { _index[v] = i++; }
        }
    };
}

namespace boost {

    template <> struct graph_traits<Glue::MyGraph> {
        // Due to Graph concept
        using vertex_descriptor      = YourLibrary::myVertex*;
        using edge_descriptor        = YourLibrary::myEdge*;
        using directed_category      = directed_tag;
        using edge_parallel_category = allow_parallel_edge_tag;
        static vertex_descriptor null_vertex() { return nullptr; }

        // Due to Vertex List concept
        struct traversal_category : vertex_list_graph_tag, incidence_graph_tag { };
        using vertex_iterator        = Glue::MyGraph::Vertices::const_iterator;
        using vertices_size_type     = std::size_t;

        // Due to Incidence Graph concept
        using out_edge_iterator = Glue::MyGraph::Edges::const_iterator;
        using degree_size_type = std::size_t;
    };

}

namespace Glue {
    // Due to Vertex List concept
    auto vertices(MyGraph const& g) {
        return std::make_pair(g._vertices.begin(), g._vertices.end());
    }

    std::size_t num_vertices(MyGraph const& g) {
        return g._vertices.size();
    }

    // Due to Incidence Graph concept
    auto source(YourLibrary::myEdge const* e, MyGraph const& g) {
        return e->source();
    }
    auto target(YourLibrary::myEdge const* e, MyGraph const& g) {
        return e->target();
    }

    auto out_edges(YourLibrary::myVertex const* v, MyGraph const& g) {
        return std::equal_range(g._edges.begin(), g._edges.end(), v, MyGraph::EdgeOrder{});;
    }
    std::size_t out_degree(YourLibrary::myVertex const* v, MyGraph const& g) {
        auto oee = std::equal_range(g._edges.begin(), g._edges.end(), v, MyGraph::EdgeOrder{});
        return std::distance(oee.first, oee.second);
    }

    // Due to BFD requiring the index_map
    auto get(boost::vertex_index_t, MyGraph const& g) {
        return boost::make_assoc_property_map(g._index);
    }
}

int main() {
    // I hate manual memory management, so let's own some objects
    auto a = std::make_unique<YourLibrary::myVertex>();
    auto b = std::make_unique<YourLibrary::myVertex>();
    auto c = std::make_unique<YourLibrary::myVertex>();
    auto ab = std::make_unique<YourLibrary::myEdge>(YourLibrary::myEdge{a.get(), b.get()});
    auto bc = std::make_unique<YourLibrary::myEdge>(YourLibrary::myEdge{b.get(), c.get()});

    // These were given in your question:
    YourLibrary::Vertices vv { a.get(), b.get(), c.get() };
    YourLibrary::Edges ee { ab.get(), bc.get() };

    // this is the glue required to fulfill the BGL concepts:
    Glue::MyGraph g(vv, ee);

    // this is showing that you can now BFS on it
    using V = boost::graph_traits<Glue::MyGraph>::vertex_descriptor;
    V start_vertex = a.get();
    std::map<V, boost::default_color_type> color_data;

    boost::breadth_first_search(g, start_vertex,
            boost::visitor(boost::default_bfs_visitor{})
            .color_map(boost::make_assoc_property_map(color_data)));
}

结论

算法有要求,只要满足要求,就可以使用所需的任何数据结构。

在这种情况下,您可能要真正确定所做的假设,并将其添加到MyGraph构造函数中:

assert(std::is_sorted(_edges.begin(), _edges.end(), EdgeOrder{}));