Kosaraju算法之直观理解

参照《算法》第四版书上相关内容

注:有向图取反后与原有向图的连通性相同。

Kosaraju算法用来解决有向图的连通性问题,算法的基本步骤:

1.对一幅有向图G,计算它的反向图GR的逆后序排列(一次dfs)。

2.按由1计算得到的顺序对G进行dfs操作,来访问所有未被标记的顶点。

3.在构造函数中,所有在同一个递归dfs()调用中被访问到的顶点都在一个强连通分量中。

疑惑在于:第一步骤为何对G进行反向后取逆后序排列?

以下图为例进行分析:

分析:若按标准顺序(0~V-1,也可按任意顺序)进行dfs,如按A~K顺序依次检查,那么递归顺序为A->B(->C)->D->E->F,结果显示ABC与DEF为同一连通分量,结果为错。错误的根源在于存在B->D这条边,而G->F这条边却并无影响。究其原因,是(1)连通分量dfs操作发生在(2)连通分量之前,因而运行到dfs(B)时,递归会进入D(即(2)连通分量中),(而实际上应该是在D处从头开始递归,count++),最终会导致(1)与(2)连通分量误并为同一连通分量。

解决方法:通过一些操作让本应该先递归的分量放在后面检查,如先对(2)连通分量进行dfs,标记后,再从(1)连通分量进行检查dfs,当执行到B处时,虽然有B->D这条边,但是D已经被标记过,则将结束(1)连通分量的检查。

现在就可以把问题抽象成为:如何安排(1)(2)(3)(4)的检查顺序(而与(1)(2)(3)(4)的内部顺序无关),使连通分量的检查成立。

这就是一个有优先级顺序的排序问题(拓扑排序解决)。优先级的限制条件为(2)应该在(1)(3)之前被dfs,(4)在(3)之前dfs(否则会产生上述问题)

操作:原有向图进行拓扑排序后为(1)(3)在(2)之前运行,(3)在(4)之前运行,所以将原有向图取反后拓扑(即与原有向图结果相反),就会满足优先级限制条件:(2)在(1)(3)之前运行,(4)在(3)之前运行

对此,算法的步骤一中进行有向图的反向后取逆后序进行了解答。

下面是Kosaraju算法的具体实现代码:

package 有向图.有向图中的强连通性;

import 有向图.图的基本定义.Digraph;
import 有向图.基于深度优先搜索的顶点排序.DepthFirstOrder;

/**
 * 强连通分量的API:
 * boolean stronglyConnected(int v,int w)  v和w是否是强连通的
 * int count()                             图中的强连通分分量的总数
 * int id(int v)                           v所在的强连通分量的标识符(0~count-1)
 */
public class KosarajuSCC {
    private boolean[] marked;   //已访问过的顶点
    private int[] id;           //强连通分量的标志符
    private int count;          //强连通分量的数量

    public KosarajuSCC(Digraph G){
        marked = new boolean[G.V()];
        id = new int[G.V()];
        DepthFirstOrder order = new DepthFirstOrder(G.reverse());       //取反向图
        for(int s : order.reversePost()){                               //逆后序排列
            if(!marked[s]){
                dfs(G,s);count++;
            }
        }
    }
    private void dfs(Digraph G,int v){
        marked[v] = true;
        id[v] = count;
        for(int w : G.adj(v)){
            if(!marked[w]){
                dfs(G,w);
            }
        }
    }

    public boolean stronglyConnected(int v,int w){
        return id[v] == id[w];
    }
    public int id(int v){
        return id[v];
    }
    public int count(){
        return count;
    }
}

补充:

拓扑排序:给定一幅有向图,将所有的顶点排序,使得所有的有向边均从排在前面的元素指向排在后面的元素。

一幅有向无环图的拓扑排序即为所有顶点的逆后序排列。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值