Kosaraju算法是求解有向图强连通分量(strong
什么是强连通分量?在这之前先定义一个强连通性(strong
首先说一些离散数学相关的结论,由强连通性的概念可以发现,这是一个等价关系。
证明:
一,按照有向图的约定,每个顶点都有到达自身的路径,即自环,即任意顶点s到s可达,满足自反性;
二,如果s与t是强连通的,则s到t存在路径,t到s存在路径,显然t与s也是强连通的,满足对称性;
三,如果r与s强连通,s与t强连通,则r与s互相可达,s与t互相可达,显然r与t也互相可达,满足传递性。
因此,强连通关系可导出一个等价类,这就是强连通分量。进一步的利用这结论可以知道,两个强连通分量之间木有交集(这个结论很重要)。事实上,图论与离散数学中的关系有非常密切的……关系。
在编程求解强连通分量时,通常做法是对顶点进行编号,拥有相同编号的顶点属于同一个强连通分量。在求解完之后,通过对编号的比较可以迅速判断两个顶点是否是强连通的。
------------------------------分割线-----------------------------------
Kosaraju算法过程上并不复杂。要求解一个有向图的强连通分量,第一步:在该图的逆图上运行DFS,将顶点按照后序编号的顺序放入一个数组中(显然,这个过程作用在DAG上得到的就是一个拓扑排序);第二步:在原图上,按第一步得出的后序编号的逆序进行DFS。也就是说,在第二次DFS时,每次都挑选当前未访问的结点中具有最大后序编号的顶点作为DFS树的树根。
Kosaraju算法的显著特征是,第一,引用了有向图的逆图;第二,需要对图进行两次DFS(一次在逆图上,一次在原图上)。而且这个算法依赖于一个事实:一个有向图的强连通分量与其逆图是一样的(即假如顶点任意顶点s与t属于原图中的一个强连通分量,那么在逆图中这两个顶点必定也属于同一个强连通分量,这个事实由强连通性的定义可证)。由于算法的时间取决于两次DFS,因此时间复杂度,对于稀疏图是O(V+E),对于稠密图是O(V²),可见这是一个线性算法。Kosaraju的结论是,在第二次DFS中,同一棵搜索树上的结点属于一个强连通分量。
证明:假设顶点s与t属于第二次DFS森林(注意,第二次是在原图上搜索)的同一棵树,r是这棵树的根结点。那么有以下两个事实:一,原图中由r可达s,这蕴含在逆图中从s到r有一条路径;二,r在逆图中的后序编号大于s(r是树根,因此r的后序编号比树中所有的其他结点的都大)。现在要证明的是在逆图中从r到s也是可达的。
好,两个事实结合起来:一,假设逆图中r到s不可达,且s到r存在路径,那么这条路径将使s的后序编号比r大,与事实一矛盾,排除;二,假设逆图中r到s存在路径,正是这条r到s的路径使得r有更大的后序编号,则r与s是强连通的,假设成立(看上去比较勉强,个人认为这应该是一个空证明)。显然,两个事实导出一个结论:逆图中,r与s互相可达。同理,r与t也互相可达,根据传递性,第二次DFS森林中同一棵树中的所有顶点构成一个强连通分量。
另一方面,会不会一个强连通分量的所有顶点没有出现在第二次DFS森林的同一颗树中呢?答案是:不会。因为根据DFS的性质,如果r与s强连通,那么由r开始的DFS必定能搜到s。
证毕。
可见Kosaraju的方法能够找出有向图的强连通分量,那么为什么这个方法可行呢?或者如何实现呢?这正是Kosaraju算法最为精妙的地方,关键在于第二次DFS选取的顺序:在第一次DFS中,将顶点按照后序编号存放,第二次DFS就按照这个顺序的逆序进行搜索,这保证每次选取的根结点(刚才证明中的r结点)都具有未访问结点中最大的后序编号,则搜索中拓展的结点的后序编号都比根结点小,这样也就满足了事实二。
补充:Kosaraju算法虽然是线性的,但是需要两次DFS,跟另外两个著名的求解强分量的算法相比,这是一个劣势。但是Kosaraju算法有个神奇之处在于:计算之后的强分量编号的顺序,刚好是该有向图K(D)(kernel
最后附上我的实现~就一目了然啦~
---------------------------分割线again--------------------------------
//
static
int
}
void
}
void
}
int
}