有向图的强连通分量

所谓有向图,指的是图中的通路具有方向性。例如: A ---> B, 表示从节点 A 到 B 有条通路, 而从 B 到 A 是不通的。有向图中的强连通分量,是指有向图的一群节点,这些节点相互之间都有路径。 求图的强连通分量都是通过对图进行深度优先搜索来实现。深度优先搜索有一些奇妙的性质。深度优先搜索最终会将图析构成一棵树(普通意义上的树),那么这棵树上的节点发现的顺序,称为时间戳,一般的算法书上会用 DFN(x) 表示节点 x 的时间戳。一个节点有两个相关的时间戳,一个是节点被深度优先搜索第一次遍历,使用记号 d[v] 表示, 也用 dfn[v] 来表示。另外一个是以该节点作为树根的树被遍历完的时间戳,使用 f[v] 来表示。下面对图的深度优先遍历会使用以下的记法:
( A   (C  (D, D)  (B,  B)  C)  A)
 1    2   3  4   5   6   7   8
   Node(x)    ( d(x),   f(x))
A         1        8
C         2        7
D         3        4
B         5        6
这里的道理是显然的,其实图的深度优先搜索就是树的先序遍历,只不过在图的深度优先遍历中,不可避免的会存在回边,但是在对图的节点做访问标志标记后,可以防止访问之前访问过的节点。在图的遍历中,存在以下四种边。
1)  树枝边 (FOREST) , 例如上图 A-->C
2)  前向边 (FORWARD)
3)  后向边 (BACKWARD) 例如上图 B-->A 
4)  横跨边 (CROSS)
下面说说 Gabow 算法,该算法定义了节点的如下一种性质:
LowLink(U) = min{ dfn(U), dfn(W)}; 以下简单记为 
L(U) = min{ d(U), d(W)};
是从 U 或者 U的后代出发用一条 BACKWARD 或者 CROSS 所能到达的同意强连通分支的顶点。
由此可见, L(U) 是U所处的强分支中出发先用 FOREST , FORWARD,最后使用 BACKWARD, CROSS 能到达的 dfn 最小的顶点的序值。对于强连通分支的树根 R,显然有 L(R) = d(R), 因此,当深度搜索从 d(X) = L(X) 的顶点X返回时,从树中移去以 X为根的所有顶点,这些顶点构成一个强连通分量。
现在来计算顶点 U 的 L(U) 值。
1)  第一次访问节点 U, 做 L(U) = d(u);
2)  通过后向边 (u, w), 做 L(U) =  min {LL(v), LL(w)}
3)  遇到横跨边 (u, w), 做 L(U) = min { LL(v), DFN(w)}
4)  从 U 的儿子 W 返回时, 做 L(U) = min { LL(v), LL(w)}
对于图的遍历序列,写成以下形式:
A B C D E F G H I J K 
例如: L(G) = dfn(D), 那么我们可以用如下形式表示:
A B C D E F G 
      |_____|
也就是从G --> D 的通路是存在的。
下面证明 Gabow 算法的正确性。
必要性:
1) 从 A ----> X , X 为非根节点,这条路径是存在的。
2) 从 X ---> Y,  只要证明 X ---> A 存在,那么
          X --> A ---> Y 存在,即 X -->Y 存在,这里 X 指任意一个节点。
3) 证明 X --> A 存在, 因为 L(X) < dfn(X), 那么 X --> P, (在序列中 P 在 X 前面),同理 L(P) < dfn (P),那么存在 P --> Q (在序列中 Q 在 P 前面),图形表示如下: 
A  B  C  D  E  F  G  H  I  J  K  L  M
 |_________| |_____| |___________| |_________|   
就是说 L(M) = dfn(J)
           L(J)  = dfn(F)
           L(F)  = dfn (D)
       L(D)  = dfn(A)
这样一系列的路径  M --> J --> F ---> D ----> A . 
算法的具体操作过程如下:
从根节点 A 出发深度优先遍历,检查的条件是当从以某一个节点为根的子树遍历完成是看 if ( L(R) == dfn(R) ),这是合理的, 可以通过模拟栈来实现。当某节点的 L(U) = dfn(U)的时候,这个节点无法访问其祖先节点,而他的所有儿子节点亦无法访问其祖先节点,因为如果有儿子能访问其祖先的话,那么根据计算法则四 L(U) 必定小于 dfn (U),因为祖先节点的 dfn 肯定要比儿子的小。反映到图上就是:
 A  B  C  D     E  F  G  H  I  J  K  
 ------------------|    |------------------------------
从 E - K 没有一个节点能访问到 A - D, 所以 E -K 是一个单独的极大连通分量。当任何一个极大连通分量分离出来后,任何指向这个连通分量的边都应该忽略。假设这个边的狐头顶点位与另外一个极大连通分量中,如果还有一个从原来连通分量到这个连通分量的边的话,那么开始遍历的时候,这个连通分量就会被包含进去。
关于四条计算规则:
1)  肯定没有问题;
2)  没有问题
4)  没有问题
简单的看看 3), 假定序列如下:
A  ( [T1]   [T2]  [T3] )
        |____________|
为什么 CROSS 需要考虑进去? 这里 [T3] ---> K , K 属于 [T1], 要看到 [T1] 还在栈中,这说明了 [T1]  A 是属于一个连通分量的。而且 [T1] 已经检查过了,而且没有拿掉,所以存在:
A   ( [T1]    [T2]    [T3])
|_____| |_______________|
还有一个值得注意的是,检查是从任何的叶子节点慢慢向上开始了。
充分性: 
使用归纳法,或者反证法,叙述起来很麻烦,想倒是很容易。
Kosaraju 算法使用 2 次 DFS , 一次是正的,一个是反的。很容易理解。对于一个真正的强连通分量,不论是正的遍历还是反的遍历,都可以把所有的节点纳入到一个树中。这一点是肯定的。该算法通过两次遍历生成 T1, T2, 然后找两者的共同元素。请注意 T1, T2 含有共同的根节点。 假定集合 P 中的顶点是T1, T2 的交集。假定 T1 是正DFS, T2 是反 DFS;
那么假定根节点为 R, 对集合的任意两点 A, B, 下面正面 A --> B 存在。
因为 A 属于 T2, 那么存在 A --> R
因为 B 属于 T1, 那么存在 R--> B, 所以存在 A -->B. 这个结论的充分性也很好证明;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值