Kosaraju算法

Kosaraju算法也许最容易理解的一个算法是Kosaraju于80年代提出的,它执行两次DFS。第一次DFS得到了关于各个SCC拓扑顺序的有关信息,而第二次DFS按照这个拓扑顺序的逆序进行DFS,从而把每个SCC分开。
算法步骤如下:第一步调用\(DFS(G)\),计算出每个结点的\(f[u]\)或者\(post[u]\)第二步计算G的转置GT (即把所有有向边(u, v)变为有向边(v, u))第三步调用\(DFS(GT )\),在主循环中按照\(f[u]\)或者\(post[u]\)递减的顺序执行DFS-VISIT,则得到的每个DFS树恰好对于一个SCC。
算法的时间复杂度为\(O(n+m)\),但注意并不是在图的所有表示法中都很容易求出转置。
为什么算法是正确的呢?
我们首先证明以下定理:
定理:\(d(U)\)\(f(U)\)表示集合U所有元素的最早发现时间和最晚完成时间,则对于G中的两个SCC C和C',如果C到C'有边,则\(f(C)>f(C')\)。证明分两种情况。
情况一:\(d(C)<d(C')\)。考虑C中第一个被发现的点\(x\),则\(x\)被发现时\(C'\)全为白色。而\(C\)\(C'\)有边,故\(x\)\(C'\)中的每个点都有白色路径。这样,\(C\)的其他点和\(C'\)的所有点都是x的后代,因此\(f(C) > f(C')\)
情况二:\(d(C)>d(C')\)。由于从\(C'\)不可到达\(C\),所以必须等\(C'\)全部访问完毕后才能访问\(C\),因此\(f(C) > f(C')\)。作为定理的推论,如果GT 中\(C\)\(C'\)有边,则\(f(C)<f(C')\)。由于G和GT 的强连通分量完全一致,所以只需要在GT 中按照\(f\)值递减的顺序执行DFS-VISIT即可。可能有读者会认为不需要转置而直接在G中按照f递增的顺序执行DFS-VISIT也能奏效,但可惜这是错误的。对于G中的两个SCC \(C\)\(C'\),如果C到\(C'\)有边,刚才的定理说的是\(f(C)>f(C')\),其中二者取的是\(C\)\(C'\)\(f\)值的最大值,而并没有说\(C\)\(C'\)中最小的f值哪个大。如果按照f递增的顺序遍历,我们无法知道究竟会先遇到\(C\)中的点还是\(C'\)中的点。如果在GT 上操作,由推论知\(f(C)<f(C')\),所以当按\(f\)值降序考虑时一定会先遇到\(C'\)中的点,从而正确的把\(C\)\(C'\)分开。

posted @ 2018-03-28 22:41  Douglas_Zhou  阅读(533)  评论(0编辑  收藏  举报