我会故意保持非正式的讨论。随意询问您是否认为某些索赔不成立或需要更多详细信息。
我希望我不会说得太啰嗦。如果我这样做,我会稍微压缩一下小说......
基础知识
该算法由一系列 DFS 遍历组成,同时在图的顶点上保持状态。反复地,选择一个算法之前没有访问过的起始顶点,并从这个节点开始一个DFS。让v 成为此 DFS 期间遇到的某个节点。让“以v为根的部分DFS”成为DFS遍历的一部分,从第一次访问开始到最后一次访问v结束。对某个节点的“访问”要求遍历的最后一条边是树边。对某个边缘的“访问”意味着它在 DFS 过程中的第一次遍历。
两个基本观察:
1。
将会有准确的k DFS 搜索,其中k 是连接组件的数量。
2。
在无向图上的 DFS 中,只有树边和后边,但没有前边或交叉边。
在无向图中,所有与顶点相关的边都是“外”边,即。可在 DFS 中遍历。因此,在连通分量#i 中的 DFS 搜索中的任何时候,遇到的任何顶点要么根本没有被访问过,要么位于当前的 DFS 树路径上。因此,DFS 到达所述顶点的边分别是树边或后边,但永远不能是前边或交叉边。
信息集
该算法维护以下有关顶点的信息。
首先,让顶点状态为三个类别之一:
-
unseen: 顶点还没有被访问过。
-
active: 顶点已经被访问过,至少有一条但不是所有的入射边都被访问过。
-
done:顶点和它的所有入射边都被访问过。该顶点将永远不会被再次访问。
定义意味着每个顶点在算法过程中将其状态从unseen 超过active 更改为done。
维护顶点处理状态的另外两个方面:
-
depth:顶点到当前DFS根的树路径距离。
-
minseen:在以顶点为根的部分 DFS 期间遇到的最小顶点“深度”。
depth 和 minseen 对于处于未见状态的顶点将是未定义的。
转动active 时,顶点的depth 将被设置并且不再改变。 minseen 将设置为 depth 并可能在此顶点保持 active 时发生变化。进入状态done 后,顶点看不到其minseen 属性的更多变化。
minseen 按照以下规则更新。
当从w 回溯到v 时(即在以w 为根的部分DFS 搜索完成后从节点w 返回到v),v.minseen 设置为@ 的较小值987654350@ 和 w.minseen,即到目前为止在 v 的部分 DFS 期间访问的任何节点之间的最近距离。
桥梁检测
如果e=(u,v) 是当前 DFS 中的桥,则以 v 为根的部分 DFS 在任何时候都不会到达比 u 更靠近 DFS 根的顶点。因此,在其状态从active 更改为done 后立即最后一次离开v 时,该节点的minseen 属性将等于depth。由于状态变化,我们知道这两个值都不会再次改变。因此e 是一座桥梁。
切换视角,如果在以 v 为根的部分 DFS 期间的任何时候,在当前 DFS 的根和 u 之间的树路径上遇到 active 节点 z(其 depth值因此小于u's 和v's),z.depth 将在 DFS 定义v.minseen 最终值上限的过程中渗透回v - 所以它不能当 DFS 最后一次离开 v 时等于 v.depth,表明 e 不是桥。
复杂性
所有顶点都按预定顺序检查一次。当遇到unseen 顶点时,DFS 开始以该顶点为根。当它结束时,这个根被标记为done 并且检查继续直到找到下一个unseen 顶点等等。
-> O(V)
由于遍历是标准的 DFS,因此每条边最多被遍历两次(如果是树边则遍历两次,否则一次)。
-> O(E)
->O(V+E)
所有其他步骤都转化为恒定数量的操作。