【问题标题】:Bridges in a connected graph连通图中的桥梁
【发布时间】:2012-06-28 10:40:12
【问题描述】:

我有一个编程任务(不是家庭作业。)我必须在图中找到桥梁。我自己做了一些工作,但无法提出任何令人满意的结果。所以我用谷歌搜索了它,我确实找到了一些东西,但我无法理解它所呈现的算法。有人可以看看这段代码并给我一个解释吗?

public Bridge(Graph G) {
    low = new int[G.V()];
    pre = new int[G.V()];
    for (int v = 0; v < G.V(); v++) low[v] = -1;
    for (int v = 0; v < G.V(); v++) pre[v] = -1;

    for (int v = 0; v < G.V(); v++)
        if (pre[v] == -1)
            dfs(G, v, v);
}

public int components() { return bridges + 1; }

private void dfs(Graph G, int u, int v) {
    pre[v] = cnt++;
    low[v] = pre[v];
    for (int w : G.adj(v)) {
        if (pre[w] == -1) {
            dfs(G, v, w);
            low[v] = Math.min(low[v], low[w]);
            if (low[w] == pre[w]) {
                StdOut.println(v + "-" + w + " is a bridge");
                bridges++;
            }
        }

        // update low number - ignore reverse of edge leading to v
        else if (w != u)
            low[v] = Math.min(low[v], pre[w]);
    }
}

【问题讨论】:

标签: algorithm graph


【解决方案1】:

Def: Bridge 是一条边,移除后会断开图的连接(或将连通分量的数量加 1)。

关于图中桥梁的一项观察;属于循环的所有边都不能是桥。因此,在诸如A--B--C--A 之类的图形中,删除任何边缘A--B、B--C 和C--A 都不会断开图形的连接。但是,对于无向图,边A--B 意味着B--A;这个边缘仍然可能是一座桥,它所在的唯一循环是A--B--A。因此,我们应该只考虑由后边缘形成的那些循环。这就是您在函数参数中传递的父信息有帮助的地方。它将帮助您不使用诸如A--B--A 之类的循环。

现在要识别后边缘(或循环),A--B--C--A 我们使用low 和pre 数组。数组pre类似于dfs算法中的visited数组;但我们不仅仅将顶点标记为已访问,而是用不同的数字标识每个顶点(根据它在 dfs 树中的位置)。 low 数组有助于识别是否存在循环。 low 数组标识当前顶点可以到达的最小编号(来自pre 数组)顶点。

让我们来处理这个图表A--B--C--D--B。

从 A 开始

dfs:   ^                 ^                 ^                 ^              ^
pre:   0 -1 -1 -1 -1  0--1 -1 -1  1  0--1--2 -1  1  0--1--2--3  1  0--1--2--3--1
graph: A--B--C--D--B  A--B--C--D--B  A--B--C--D--B  A--B--C--D--B  A--B--C--D--B
low:   0 -1 -1 -1 -1  0--1 -1 -1  1  0--1--2 -1  1  0--1--2--3  1  0--1--2--3->1

此时,您在图中遇到了一个循环/循环。在您的代码中,if (pre[w] == -1) 这次将是错误的。因此,您将进入 else 部分。 if 语句检查B 是否是D 的父顶点。不是,所以D 会将B 的pre 值吸收到low 中。继续这个例子,

dfs:            ^
pre:   0--1--2--3
graph: A--B--C--D
low:   0--1--2--1   

D 的 low 值通过代码 low[v] = Math.min(low[v], low[w]); 传播回 C。

dfs:         ^           ^           ^
pre:   0--1--2--3--1  0--1--2--3--1  0--1--2--3--1
graph: A--B--C--D--B  A--B--C--D--B  A--B--C--D--B
low:   0--1--1--1--1  0--1--1--1--1  0--1--1--1--1

现在,循环/循环已被识别,我们注意到顶点A 不是循环的一部分。因此,您打印出A--B 作为桥梁。代码low['B'] == pre['B'] 表示到B 的边将是一座桥。这是因为,我们可以从B 到达的最低顶点是B 本身。

希望这个解释有所帮助。

【讨论】:

  • 这个可视化对我帮助很大!其他网站都没有用!谢谢@deebee
【解决方案2】:

不是一个新的答案,但我在 Python 中需要它。以下是无向 NetworkX Graph 对象G 的算法翻译:

def bridge_dfs(G,u,v,cnt,low,pre,bridges):
    cnt    += 1
    pre[v]  = cnt
    low[v]  = pre[v]

    for w in nx.neighbors(G,v):
        if (pre[w] == -1):
            bridge_dfs(G,v,w,cnt,low,pre,bridges)

            low[v] = min(low[v], low[w])
            if (low[w] == pre[w]):
                bridges.append((v,w))

        elif (w != u):
            low[v] = min(low[v], pre[w])

def get_bridges(G):
    bridges = []
    cnt     = 0
    low     = {n:-1 for n in G.nodes()}
    pre     = low.copy()

    for n in G.nodes():
         bridge_dfs(G, n, n, cnt, low, pre, bridges)

    return bridges # <- List of (node-node) tuples for all bridges in G

注意 Python 对大图的递归深度限制器...

【讨论】:

    【解决方案3】:

    不是一个新的答案,但我需要这个用于 JVM/Kotlin。这是一个依赖com.google.common.graph.Graph的翻译。

    /**
     * [T] The type of key held in the [graph].
     */
    private class BridgeComputer<T>(private val graph: ImmutableGraph<T>) {
        /**
         * Counter.
         */
        private var count = 0
        /**
         * `low[v]` = Lowest preorder of any vertex connected to `v`.
         */
        private val low: MutableMap<T, Int> =
            graph.nodes().map { it to -1 }.toMap(mutableMapOf())
        /**
         * `pre[v]` = Order in which [depthFirstSearch] examines `v`.
         */
        private val pre: MutableMap<T, Int> =
            graph.nodes().map { it to -1 }.toMap(mutableMapOf())
    
        private val foundBridges = mutableSetOf<Pair<T, T>>()
    
        init {
            graph.nodes().forEach { v ->
                // DO NOT PRE-FILTER!
                if (pre[v] == -1) {
                    depthFirstSearch(v, v)
                }
            }
        }
    
        private fun depthFirstSearch(u: T, v: T) {
            pre[v] = count++
            low[v] = checkNotNull(pre[v]) { "pre[v]" }
            graph.adjacentNodes(v).forEach { w ->
                if (pre[w] == -1) {
                    depthFirstSearch(v, w)
                    low[v] =
                        Math.min(checkNotNull(low[v]) { "low[v]" }, checkNotNull(low[w]) { "low[w]" })
                    if (low[w] == pre[w]) {
                        println("$v - $w is a bridge")
                        foundBridges += (v to w)
                    }
                } else if (w != u) {
                    low[v] =
                        Math.min(checkNotNull(low[v]) { "low[v]" }, checkNotNull(pre[w]) { "pre[w]" })
                }
            }
        }
    
        /**
         * Holds the computed bridges.
         */
        fun bridges() = ImmutableSet.copyOf(foundBridges)!!
    }
    

    希望这能让某人的生活更轻松。

    【讨论】:

      【解决方案4】:

      假设你有一个优势 (c,d),你必须找出它是否是一座桥
      有几种方法可以解决这个问题,但让我们专注于一种。

      • 从c开始,你必须做一个BFS。
      • 如果有边 c-d 则不要访问它。
      • 通过访问布尔值来跟踪顶点。

      最后,如果你发现 d 被访问了,这意味着通过删除 c-d 我们仍然可以从源 c 访问 d,因此 c-d 不是桥。 这是上述的简短实现:

      int isBridge(int V, ArrayList<ArrayList<Integer>> adj,int c,int d)
      {
          Queue<Integer> q = new LinkedList<>();
          boolean visited[] = new boolean[V];
          ArrayList<Integer> ls = new ArrayList<>();
          
          q.add(c);
          
          while(!q.isEmpty()) {
              
              Integer v = q.remove();
              
              if(visited[v])
                  continue;
      
              visited[v] = true;
              ls.add(v);
              
              for(Integer e: adj.get(v)) {
                  
                  if(visited[e] || (c == v && d == e))
                      continue;
                  q.add(e);
              }
          }
          
          if(visited[d] == true)
              return 0;
          return 1;
          
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-06-17
        • 1970-01-01
        • 1970-01-01
        • 2010-10-22
        • 1970-01-01
        • 2011-08-26
        相关资源
        最近更新 更多