我有一个编程任务(不是家庭作业。),我必须在图中找到桥梁。我自己做了一些工作,但无法想出任何令人满意的东西。所以我用Google搜索,我找到了一些东西,但我无法理解它所呈现的算法。有人可以看看这段代码并给我一个解释。?
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]);
}
}
答案 0 :(得分:29)
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
的{{1}}值吸收到pre
中。继续这个例子,
low
dfs: ^
pre: 0--1--2--3
graph: A--B--C--D
low: 0--1--2--1
的{{1}}值通过代码low
传播回D
。
C
现在,识别出循环/循环,我们注意到顶点low[v] = Math.min(low[v], low[w]);
不是循环的一部分。因此,您打印出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
本身。
希望这个解释有所帮助。
答案 1 :(得分:1)
不是新答案,但我需要在Python中使用它。以下是无向NetworkX图形对象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的大型图的递归深度限制器......
答案 2 :(得分:1)
不是一个新的答案,但我需要这个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)!!
}
希望这会让某人的生活更轻松。
答案 3 :(得分:0)
假设你有一条边 (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;
}