Kosaraju Kosaraju算法

问题

用Kosaraju算法求有向图DGDG的所有强连通分支。

解法

首先用DFS搜索所有顶点,然后按照DFS逆序对每个顶点进行DFS。根据强连通分支的定义可知。逆序时任意顶点viv_i按照DFS搜索可以到达的顶点都和它属于同一强连通分支。Kosaraju算法使用并查集将顶点分配给不同的强连通分支。

Kosaraju算法分为33步:

(1)(1) 初始化空队列queuequeue,该队列记录有向图中所有顶点的DFS顺序;

(2)(2) 类似DFS和二叉树的后续遍历,遍历图DGDG所有顶点viv_i,对每个顶点viv_i进行搜索操作:2.1)2.1) 若顶点viv_i已被染红则跳过,否则将viv_i染红并加入队列queuequeue2.2)2.2) 选取viv_i的邻节点中未被染红的邻节点vjv_j,递归进行相同的搜索操作,直到无法搜索到更多未被染红的顶点;

(3)(3) 类似DFS和并查集算法,首先令队列queuequeue中的任意顶点viv_i属于根节点为viv_i(即自己)的强连通分支,即设父指针father(i)=ifather(i) = i。逆序遍历队列queuequeue所有顶点,对每个顶点viv_i进行聚合:3.1)3.1) 若顶点viv_i存在father(i)ifather(i) \neq i则跳过,表示该顶点已经被分配到某个强连通分支;3.2)3.2) 选取viv_i的邻节点中满足father(j)=jfather(j) = j的邻节点vjv_j,将vjv_j分配给以viv_i为根节点的强连通分支,即father(j)=ifather(j) = i

(3)(3)步结束后,每个顶点都被分配给某个强连通分支。查询某两个顶点vi,vjv_i, v_j是否属于同一个强连通分支,判断father(i)=father(j)father(i) = father(j)是否成立即可,同一强连通分支中的所有顶点的父节点相同,最终所有顶点都被分配到某个强连通分支。

下图演示有向图的搜索操作和分配操作:

上图进行DFS搜索后,得到的队列为[0,4,3,1,2,7,6,5,8][0, 4, 3, 1, 2, 7, 6, 5, 8],逆序为[8,5,6,7,2,1,3,4,0][8, 5, 6, 7, 2, 1, 3, 4, 0]。第(2)(2)步的DFS搜索过程如下图:

按照逆序DFS遍历所有顶点,得到两个强连通分支[8][8][0,1,2,3,4,5,6,7][0, 1, 2, 3, 4, 5, 6, 7]

该算法时间复杂度为O(V)O(\| V \|)

Strongly-Connectivity Algorithm

Introduction To Algorithms

源码

Kosaraju.h

Kosaraju.cpp

测试

KosarajuTest.cpp

Last updated