2

我有一组边 E,我想知道是否可以安全地删除 E 中的边 i,这意味着如果我将其从图中删除,则该图仍应连接。

在我的理解中,这意味着边 i 必须位于一个圆上。输出应该是我无法删除的所有边的索引列表。

问题:

我的不同解决方案似乎做了正确的事情,但是太慢了(效率低下)。

我的解决方案之一是:

1. Loop through all edges i in E
  2. Loop through all edges x in V
    3. Add edge x to the graph (excluding edge i) until nodes of i are connected or end reached
  4. If no connection possible, edge is not removable and added to the list

这种方式太慢了。

然后我决定重写我的代码并使用广度优先搜索来查看没有边缘 i 的另一条路径是否可行。

我认为它的性能足够好,但似乎不是。要么我以非常糟糕的方式实现,要么这也是该任务的错误算法。

这是我拥有的 C++ 代码中的算法(删除了一些不重要的部分):

struct connection {
    int a, b;
};

void expand(int x, connection *&arr, std::set<int> &exp, int size) {

    for (int i = 0; i < size; i++) {
        if (x == arr[i].a) {
            exp.insert(arr[i].b);
        }
        else if (x == arr[i].b) {
            exp.insert(arr[i].a);
        }
    }

    return;
}

// recursive breadth-first-seach

bool BFSr(std::set<int> &group, std::set<int> &visited, int goal, connection *&arr, int size) {
    if (group.empty()) return false;
    if (group.find(goal) != group.end()) return true;
    std::set<int> tempa;

    for (std::set<int>::iterator it = group.begin(); it != group.end(); ++it) {
        expand(*it, arr, tempa size);
    }

    for (std::set<int>::iterator it = visited.begin(); it != visited.end(); ++it) {
        tempa.erase(*it);
    }

    tempb = visited;
    tempb.insert(group.begin(), group.end());
    return BFSr(tempa, tempb, goal, arr, size);
}

bool BFS(int start, int goal, connection *&arr, int size) {
    std::set<int> tempa;
    std::set<int> tempb;
    tempa.insert(start);
    return BFSr(tempa, tempb, goal, arr, size);
}

int main()
{

    connection *arr = new connection[m];
    connection *con = new connection[m - 1];

    // fill arr with connections arr.a < arr.b ....

    for (int j = 0; j < (m - 1); j++) {
        con[j] = arr[j + 1];
    }

    // 1. edge for performance reasons extra
    if (!BFS(arr[0].a, arr[0].b, con, (m - 1))) {
        // connection is important therefore add to list
        printf(" %d", 1);
    }

    // Look if nodes still connected after removing connection
    for (int s = 1; s < m; s++) {

        con[s - 1] = arr[s - 1];

        if (!BFS(arr[s].a, arr[s].b, con, (m-1))) {
            // connection is important therefore add to list
            printf(" %d", s + 1);
        }
    }

    printf("\n");


    free(arr);
    free(con);

    return 0;
}

你知道我的任何解决方案可以让它更快吗,或者你知道我的问题有更好的算法吗?

4

3 回答 3

1

删除断开两个连接组件的边称为,并且存在用于查找图中所有桥的线性时间算法(通常基于深度优先搜索)。Wikipedia 文章列出了其中之一(由于 Tarjan)作为示例。这篇论文还给出了一个简单的算法来列出图中所有的桥,并且似乎比 Tarjan 的算法简单一些。

希望这可以帮助!

于 2015-04-24T23:01:33.157 回答
1

这是您的算法的另一个版本(我想您将免费获得图形和各种算法的行业标准优化实现):

我将此图像称为图形模型。要点(来自这篇文章

  1. 找到关节点
  2. 所有具有单个外边的点都是关节点(增强图不会返回这些)-这些边是自动桥接边
  3. 对于每个关节点-在每个外边上循环,如果外边已经没有桥接边-然后删除边并检查图形组件并再次添加边

最后它将打印Edge(a,g) 连接图中的组件

在此处输入图像描述

#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/biconnected_components.hpp>
#include <boost/graph/connected_components.hpp>

#include <functional>
#include <string>
#include <vector>
#include <unordered_map>
#include <unordered_set>

typedef boost::adjacency_list <boost::vecS, boost::vecS, boost::undirectedS> Graph;
typedef boost::graph_traits<Graph>::vertex_descriptor vertex_t;
typedef boost::graph_traits<Graph>::edge_descriptor edge_t;

//  reference:
//  http://lists.boost.org/boost-users/2005/08/13098.php
//
struct edge_t_hasher
{
    std::size_t operator()(const edge_t& e) const
    {
        auto prop = e.get_property();
        std::hash<decltype(prop)> hasher;
        return hasher(prop);
    }
};

typedef std::unordered_set<edge_t, edge_t_hasher> UnorderedBoostEdgeSet;

Graph getGraph()
{
    Graph g;

    vertex_t aVtx = boost::add_vertex(g);
    vertex_t bVtx = boost::add_vertex(g);
    vertex_t cVtx = boost::add_vertex(g);
    vertex_t dVtx = boost::add_vertex(g);
    vertex_t eVtx = boost::add_vertex(g);
    vertex_t fVtx = boost::add_vertex(g);
    vertex_t gVtx = boost::add_vertex(g);
    vertex_t hVtx = boost::add_vertex(g);
    vertex_t iVtx = boost::add_vertex(g);

    boost::add_edge(dVtx, cVtx, g);
    boost::add_edge(dVtx, bVtx, g);
    boost::add_edge(cVtx, bVtx, g);
    boost::add_edge(aVtx, bVtx, g);
    boost::add_edge(bVtx, eVtx, g);
    boost::add_edge(eVtx, fVtx, g);
    boost::add_edge(aVtx, fVtx, g);
    boost::add_edge(aVtx, gVtx, g);// edge connecting components
    boost::add_edge(gVtx, iVtx, g);
    boost::add_edge(gVtx, hVtx, g);
    boost::add_edge(hVtx, iVtx, g);

    return g;
}

UnorderedBoostEdgeSet bridgingEdgesForGraph(const Graph& graph)
{
    UnorderedBoostEdgeSet bridgeEdges;

    std::unordered_set<vertex_t> articulationVertices;
    boost::articulation_points(graph, std::inserter(articulationVertices, articulationVertices.end()));

    //  add all the single connected vertices to the articulation vertices
    auto vtxIters = boost::vertices(graph);
    for (auto it = vtxIters.first, end = vtxIters.second; it != end; ++it)
    {
        if (boost::out_degree(*it, graph) == 1)
            bridgeEdges.insert(*(boost::out_edges(*it, graph).first));
    }

    std::vector<vertex_t> componentsInGraph(boost::num_vertices(graph));
    int numComponentsInGraph = boost::connected_components(graph, &componentsInGraph[0]);

    //  for each articulation vertex now get edges and check if removing that
    //  edge causes graph change in connected components
    //

    //  copy the graph- so we can iterate over the outeges of vertices
    //  we will be fiddling with the copy- since the vtx descriptors are
    //  ints- they stay same across copy and removing edge operation
    auto graph2 = graph;
    for (auto vtx : articulationVertices)
    {
        auto outEdges = boost::out_edges(vtx, graph);
        for (auto it = outEdges.first, end = outEdges.second; it != end; ++it)
        {
            auto edge = *it;
            if (bridgeEdges.find(edge) != bridgeEdges.end())
                continue;

            //  map this edge to graph2 edge- for removal and eventual addition
            auto src = boost::source(edge, graph);
            auto tgt = boost::target(edge, graph);

            auto edge2 = boost::edge(src, tgt, graph2).first;

            boost::remove_edge(edge2, graph2);
            std::vector<vertex_t> componentsInGraph2(boost::num_vertices(graph2));
            int numComponentsInGraph2 = boost::connected_components(graph2, &componentsInGraph2[0]);

            // bridging edge- graph #components changed
            if (numComponentsInGraph != numComponentsInGraph2)
                bridgeEdges.insert(edge);

            //  add the edge back to graph2
            boost::add_edge(src, tgt, graph2);
        }
    }

    return bridgeEdges;
}

int main()
{
    const Graph& graph = getGraph();
    const auto& bridgingEdges = bridgingEdgesForGraph(graph);

    const char* array = {"ABCDEFGHI"};
    for (auto edge : bridgingEdges)
    {
        std::cout << "Edge(" << array[boost::source(edge, graph)] << ", " << array[boost::target(edge, graph)] 
                  << ") is a bridging edge" << std::endl;
    }

    return 0;
}
于 2015-04-24T20:19:22.900 回答
0

与此同时,我发现了这些特殊边缘的名称:桥梁。

因此,我找到了一个提供 DFS 算法来查找所有桥梁的站点。

这对我的目的来说已经足够快了。

用于在无向图中查找桥的 DFS 算法

谢谢萨朗,你的帖子让我找到了该网站的正确搜索词。

于 2015-04-24T22:22:07.777 回答