从切割顶点查找桥的算法

时间:2018-02-11 20:57:38

标签: algorithm graph

我试图找出在无向图中找到桥的切割顶点的最佳方法。我应该使用dfs吗?如果是这样我怎么去确定它是否是一座桥?我知道如何在纸上做,但不在代码中。

1 个答案:

答案 0 :(得分:1)

我不认为你可以从知道切割顶点的任何好处来确定桥梁是什么。

图形可以切割顶点但没有桥梁: enter image description here

图形可以没有切割顶点但仍然是桥梁:

enter image description here

即使两个切割顶点之间的边缘也不能保证成为桥梁:

enter image description here

寻找桥梁的算法

桥是那些不在循环中的边缘。

您可以在图中使用深度优先搜索(DFS)从图中任意选择的节点中找到周期:

  • 每当遇到已访问过的节点时,您就找到了一个循环。我们可以将此节点称为"循环"节点。此循环节点必须位于从起始节点后跟DFS的当前路径上。

  • 将此循环的所有边标记为循环。或者,或者是桥梁。该标记可以在DFS的回溯部分期间完成。如果"周期"节点仍在路径上方,标记当前边缘。一旦我们进一步向后回溯比访问的节点,就不再执行该标记。

  • 在回溯到循环节点之前,我们可以找到几个循环:我们可以有几个循环节点待定,但只需要记住路径中最高的循环节点。为此,我们可以存储"深度"这样的循环节点在DFS搜索路径中具有。

这是执行DFS并识别网桥的伪代码:

visited = new Map # keyed by vertices, giving depth at which a vertext was visited
bridges = new Set(edges) # collection of all edges

func dfs(previous, current, depth):
    visited.set(current, depth)
    cycleParent = depth + 1 # This means: no cycle detected
    for edge in edgesFrom(current):
        next = nodesOnEdge(edge)[0] # get one vertex on this edge
        if next == current: next = nodesOnEdge(edge)[1] # take the other vertex 
        if next != previous: # Not yet visited this edge
            # Either already visited this node, or recursion is initiated
            cycle = visited.get(next)
            if not cycle: cyle = dfs(current, next, depth+1)
            if cycle <= depth: bridges.remove(edge) # Edge is on a cycle
            if cycle < cycleParent: cycleParent = cycle
    return cycleParent

dfs(null, nodes[0], 1) # Start from any node, and depth = 1

# At the end of this process: bridges contains the answer.

根据您拥有的数据结构,您必须实现两个功能:

  • edgesFrom(node):返回入射到给定顶点的边
  • nodesOnEdge(edge):返回由给定边连接的两个顶点(作为数组)。

这是JavaScript中的一个实现,只是为了演示一个例子:

&#13;
&#13;
function getBridges(graph) {
    const visited = new Map,
        bridges = new Set(graph.edges().toArray());

    function dfs(previous, current, depth) {
        visited.set(current, depth);
        let cycleParent = depth+1; // = No cycle
        for (let edge of current.connectedEdges().toArray()) {
            // Get other node on this edge
            const next = edge.source() === current ? edge.target() : edge.source();
            if (next === previous) continue; // Already visited this edge
            // Either already visited this node, or recursion is initiated
            const cycle = visited.get(next) || dfs(current, next, depth+1);
            if (cycle <= depth) bridges.delete(edge); // Edge is on a cycle
            if (cycle < cycleParent) cycleParent = cycle;
        }
        return cycleParent;
    }
    dfs(null, graph.nodes()[0], 1);
    return bridges;
}

function pairsToGraph(pairs) {
    // Initialise Cytoscape
    const cy = cytoscape({ container: document.getElementById('cy') });
    // Add points and edges to graph
    cy.add(Array.from(new Set([].concat(...pairs)), id => ({data:{id}})));
    cy.add(pairs.map(pair => ({data:{id: pair.join('_'), source: pair[0], target: pair[1]}})));
    // Render graph
    cy.layout({ name: 'cola', animate: true }).run();
    return cy;
}

// Define graph
const pairs = [
    [0,1], [1,2], [2,3], [3,1], [3,4], [4,5], [5,6], [6,3],
    [6,7], [7,3], [7,8], [8,9], [9,10], [10,11], [11,12], [12,9],
    [8,13], [13,14], [14,15], [15,16], [16,14], [16,13]  
];

// Use cytoscape for creating a graph object and its rendering
const cy = pairsToGraph(pairs);

// Get the bridges, and highlight them
const bridges = getBridges(cy);
for (const bridge of bridges) bridge.select();
&#13;
#cy {
    width: 100%;
    height: 100%;
    position: absolute;
    top: 0px;
    left: 0px;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.2.8/cytoscape.min.js"></script>
<script src="http://marvl.infotech.monash.edu/webcola/cola.v3.min.js"></script>
<script src="https://cdn.rawgit.com/cytoscape/cytoscape.js-cola/2.0.0/cytoscape-cola.js"></script>
<div id="cy"></div>
&#13;
&#13;
&#13;