Boost.Graph如何合并两个顶点/合约边

时间:2013-07-20 12:54:00

标签: c++ boost graph boost-graph

如何在Boost.Graph中合并两个顶点/合约边缘?

我需要将边缘从顶点A移动到顶点B,并删除顶点A - 是否有任何内置函数?或者也许adjacency_list有一些特别之处?

如果没有这样的功能 - 为什么呢?我认为这是常见的图形操作。

编辑:我知道可以手动完成,但是有一些极端情况(比如保留边缘属性),这就是为什么它适合在库中。

我最感兴趣的是知道Boost.Graph是否已经有了这个操作(也许有一些奇特的名字?)。如果不是 - 为什么这样的原始操作/算法不在 Graph 库中。也许我错过了一些东西,而且这种操作不是原始的或很少使用。

我不需要半生不熟的快速概念验证

3 个答案:

答案 0 :(得分:8)

半生不熟的快速概念验证

您可以在根据add_edge()

定义的图表上使用remove_vertex()adjacency_list
#include <iostream>
#include <iterator>
#include <boost/graph/adjacency_list.hpp>

using V = unsigned;
using E = std::pair<V, V>;
using G = boost::adjacency_list<boost::vecS, boost::vecS>;

void print_graph(G const& g)
{
    auto vs = boost::vertices(g);
    for (auto vit = vs.first; vit != vs.second; ++vit) {
        auto neighbors = boost::adjacent_vertices(*vit, g);
        for (auto nit = neighbors.first; nit != neighbors.second; ++nit)
            std::cout << "{" << *vit << "," << *nit << "}" << ", ";
    }
    std::cout << "\n";
}

void contract_vertices(V b, V a, G& g)
{
    auto be = boost::adjacent_vertices(b, g);
    for (auto beit = be.first; beit != be.second; ++beit)
        add_edge(a, *beit, g);
    remove_vertex(b, g);
}

int main()
{
    // named vertices
    auto const A = V { 1 };
    auto const B = V { 2 };

    // construct the graph
    auto e = std::vector<E> { { A, 3 }, { B, 4 } };
    auto g = G { std::begin(e), std::end(e), 4 };

    print_graph(g);
    contract_vertices(B, A, g);    
    print_graph(g);
}

Live example 打印

  

{1,3},{2,4},
  {1,2},{1,3},

输出不是您所期望的,因为顶点标记也会更新以反映B的删除,这会导致节点3和4现在标记为2和3。

库质量代码的要求

用于收缩顶点uv的通用库质量算法通常应至少考虑以下极端情况

  • 删除(u,v)和(v,u)边缘;
  • 将所有u和v外边缘与公共目标合并;
  • 将所有u和v in-edge与公共源合并;
  • 将剩余的u外边缘移动到v;
  • 将其余的你的边缘移到v;

Boost.Graph为此类操作提供了所有必需的原语:in_edges()out_edges()add_edge()clear_vertex()remove_vertex()。对于无向图,这些项中的一些可以在一个步骤中完成,而对于有向图,通常需要两个步骤。

除了这些算法步骤之外,还应该定义合并或移动边缘的语义。例如。他们的财产会发生什么?这类似于例如合并两家公司:联合公司应该以何种名义运作?

为什么Boost.Graph(尚未)提供contract_vertices()

TL; DR 我不知道。但我可以推测。主要是,应该指定假定contract_vertices()的接口。除了要缩小的两个顶点以及它们所属的图形类型之外,还应该在边缘属性上定义合并和移动操作。从理论上讲,应该可以使用合适的模板参数对一般算法进行此操作。

答案 1 :(得分:-1)

手动执行,您应手动删除每个b边,而不是顶点:

void collapse_vertices(V b, V a, G& g)
{
    auto be = boost::adjacent_vertices(b, g);
    for (auto beit = be.first; beit != be.second; ++beit)
    {
        add_edge(a, *beit, g);
        remove_edge(b, *beit, g);
    }
}

发出您想要的{1,3}, {1,4},

我不知道为什么它不包括在我的知识中(在我的知识中),但是这个功能就是它。

答案 2 :(得分:-1)

库中没有通用函数,因为通用函数不可能知道“极端情况”需要做什么。如果顶点X对顶点A和B都有边缘怎么办?该函数应该简单地删除X-A,还是应该删除X-B并将X-A移动到X-B?如果从X到A的边(被删除的顶点)具有必须保留或修改的属性,该怎么办?只有应用程序代码知道在删除或移动边时如何处理属性

正如qble所暗示的那样,“委派”这些决定毫无意义。如果关于如何处理已删除边的属性的决定被“委托”给应用程序代码,则应用程序代码将必须查找并循环必须删除的边。因此,它必须重复与泛型函数相同的工作。一旦它完成了每个删除边的属性,并且不打扰调用泛型函数,它也可以进行边删除本身。