有向图的强连通分量
一.
强连通分量:在有向图中,如果对于每一对a,b,且a != b,从a到b和从b到a都存在路径,则称该有向图时强连通图。有向图中的极大强连通子图称作有向图的强连通分量。
二.
深度优先搜索算法是求有向图的强连通分量的一个有效的方法。
深度优先搜索算法:
1 Boolean visited [ MAX ]; // 访问标志数组 2 3 Status ( *VisitFunc ) ( int v ); // 函数变量 4 5 void DFSTraverse ( Graph G, Status ( *Visit ) ( int v) ) { 6 7 // 对图 G 作深度优先遍历 8 9 VisitFunc = Visit; 10 11 // 使用全局变量 VisitFunc,使 DFS 不必设函数指针参数 12 13 for ( v = 0; v < G.vexnum; ++v ) 14 15 visited[v] = FALSE; // 访问标志数组初始化 16 17 for ( v = 0; v < G.vexnum; ++v ) 18 19 if ( ! visited[v] ) DFS ( G, v ); // 对尚未访问的顶点调用 DFS 20 21 } // DFSTraverse 22 23 void DFS ( Graph G, int v ) { 24 25 // 从第 v 个顶点出发,递归地深度优先遍历图 G 26 27 visited [v] = TRUE; 28 29 VisitFunc (v); // 访问第 v 个顶点 30 31 for ( w = FirstAdjVex ( G, v ); w; w = NextAdjVex ( G, v, w ) ) 32 33 if ( ! vsited[w] ) DFS ( G, w ); // 对 v 的尚未访问的邻接顶点 w 递归调用 DFS 34 35 } // DFS
三.
求有向图中的强连通分量思路
1.Tarjan算法
算法的基本思想如下:任选一结点开始进行深度优先搜索(若深度优先搜索结束后仍有未访问的结点,则再从中任选一点再次进行)。搜索过程中已访问的结点不再访问。搜索树的若干子树构成了图的强连通分量。
结点按照被访问的顺序存入栈中。从搜索树的子树返回至一个结点时,检查该结点是否是某一强连通分量的根结点(见下)并将其从栈中删除。如果某结点是强连通分量的根,则在它之前出栈且还不属于其他强连通分量的结点构成了该结点所在的强连通分量。
根结点的性质
算法的关键在于如何判定某结点是否是强连通分量的根。注意“强连通分量的根”这一说法仅针对此算法,事实上强连通分量是没有特定的“根”的。在这里根结点指深度优先搜索时强连通分量中首个被访问的结点。
为找到根结点,我们给每个结点v一个深度优先搜索标号v.index,表示它是第几个被访问的结点。此外,每个结点v还有一个值v.lowlink,表示从v出发经有向边可到达的所有结点中最小的index。显然v.lowlink总是不大于v.index,且当从v出发经有向边不能到达其他结点时,这两个值相等。v.lowlink在深度优先搜索的过程中求得,v是强连通分量的根当且仅当v.lowlink = v.index。
algorithm tarjan is input: 图 G = (V, E) output: 以所在的强连通分量划分的顶点集 index := 0 S := empty // 置栈为空 for each v in V do if (v.index is undefined) strongconnect(v) end if function strongconnect(v) // 将未使用的最小index值作为结点v的index v.index := index v.lowlink := index index := index + 1 S.push(v) // 考虑v的后继结点 for each (v, w) in E do if (w.index is undefined) then // 后继结点w未访问,递归调用 strongconnect(w) v.lowlink := min(v.lowlink, w.lowlink) else if (w is in S) then // w已在栈S中,亦即在当前强连通分量中 v.lowlink := min(v.lowlink, w.index) end if // 若v是根则出栈,并求得一个强连通分量 if (v.lowlink = v.index) then start a new strongly connected component repeat w := S.pop() add w to current strongly connected component until (w = v) output the current strongly connected component end if end function
变量index是深度优先搜索的结点计数器。S是栈,初始为空,用于存储已经访问但未被判定属于任一强连通分量的结点。注意这并非一个一般深度优先搜索的栈,结点不是在以它为根的子树搜索完成后出栈,而是在整个强连通分量被找到时。
最外层循环用于查找未访问的结点,以保证所有结点最终都会被访问。strongconnect进行一次深度优先搜索,并找到结点v的后继结点构成的子图中所有的强连通分量。
当一个结点完成递归时,若它的lowlink仍等于index,那么它就是强连通分量的根。算法将在此结点之后入栈(包含此结点)且仍在栈中的结点出栈,并作为一个强连通分量输出。