数据结构-图之强连通
在一个无向图G中,若从顶点v_i到顶点v_j有路径相连(当然从v_j到v_i也一定有路径),则称v_i和v_j是连通的。如果G是有向图,那么连接v_i和v_j的路径中所有的边都必须同向。如果图中任意两点都是连通的,那么图被称作连通图。图的连通性是图的基本性质。
连通分量:无向图G的一个极大连通子图称为G的一个连通分量(或连通分支)。连通图只有一个连通分量,即其自身;非连通的无向图有多个连通分量。
初级通路:通路中所有的顶点互不相同。初级通路必为简单通路,但反之不真。
强连通图:有向图G= (V,E)中,若对于V中任意两个不同的顶点x和y,都存在从x到y以及从y到x的路径,则称G是强连通图(Strongly Connected Graph)。相应地有强连通分量(Strongly Connected Component)的概念。强连通图只有一个强连通分量,即是其自身;非强连通的有向图有多个强连通分量。
强连通图指每一个顶点皆可以经由该图上的边抵达其他的每一个点的有向图。意即对于此图上每一个点对(Va,Vb),皆存在路径Va→Vb以及Vb→Va。强连通分量则是指一张有向图 G 的极大强连通子图 G‘。如果将每一个强连通分量缩成一个点,则原图 G 将会变成一张有向无环图。一张图被称为有向无环图当且仅当此图不具有点集合数量大于一的强连通分量,因为有向环即是一个强连通分量,而且任何的强连通分量皆具有至少一个有向环。
Kosaraju算法、Tarjan算法、Gabow算法皆为寻找有向图强连通分量的有效算法。但是由于在Tarjan 算法和 Gabow 算法的过程中,只需要进行一次的深度优先搜索,因而相对 Kosaraju 算法较有效率。
寻找强连通分量的算法,也可以用来解 2-SAT(2-satisfiability) 问题。Aspvall, Plass & Tarjan (1979)
根据 Robbins理论,当一个无向图为双连接图时,也会形成强连通(strong connectivity)。
缩点
由于强连通分量的特殊性,在一些实际应用中,会将每个强连通分量看成一个点,然后进行处理。这样做主要是为了降低图的复杂度,特别是在强连通分量规模大、数量多的情况中,利用“缩点”能大幅度降低图的复杂度。缩点后得到的图,必定是DAG。
将一个大图分解成多个连通分量,某些有向图算法可以分别在各个连通分量上独立运行,最后再根据分量之间的关系将所有的解组合起来。
(青玉案 元夕 [辛弃疾]
东风夜放花千树,更吹落星如雨。宝马雕车香满路,凤箫声动,玉壶光转,一夜鱼龙舞。
蛾儿雪柳黄金缕,笑语盈盈暗香去。众里寻他千百度,蓦然回首,那人却在,灯火阑珊处。)
Kosaraju算法(1978)
Kosaraju的算法(也称为Kosaraju-Sharir算法)是线性时间的算法来找到一个有向图的强连通分量。Aho, Hopcroft 和Ullman相信这个算法是由S. Rao Kosaraju在1978在一个未发表的论文上提出的。相同的算法还从Micha Sharir 1981年自己出版的书上被单独的发现,这个算法利用了一个事实,即转置图(同图中的每边的方向相反)具有和原图完全一样的强连通分量。
转置图,既逆图,是新创建的图,其每条边都与原图边的方向相反。
Kosaraju算法为了找到强连通分量,首先对图G运行DFS,计算出各顶点完成搜索的时间f;然后计算图的逆图GT,对逆图GT也进行DFS搜索,但是这里搜索时顶点的访问次序不是按照顶点标号的大小,而是按照各顶点f值由大到小的顺序;逆图DFS所得到的森林即对应连通区域。
上面我们提及原图G的逆图GT,其定义为GT=(V,ET),ET={(u,v):(v,u)∈E}}。也就是说GT是由G中的边反向所组成的,通常也称之为图G的转置。在这里值得一提的是,逆图GT和原图G有着完全相同的连通分支,也就说,如果顶点s和t在G中是互达的,当且仅当s和t在GT中也是互达的。
伪代码
1、对原图G进行深度优先遍历,记录每个节点的离开时间num[i]。
2、求得原图的逆图GT。
3、选择具有最晚离开时间的顶点,对逆图GT进行遍历,删除能够遍历到的顶点,这些顶点构成一个强连通分量。
4、如果还有顶点没有删除,继续步骤3,否则算法结束.
Kosaraju算法的流程简单,但是需要对图(和逆图)进行两次DFS搜索,而且读逆图的DFS搜索中顶点的访问顺序有特定的限制。
Tarjan算法
Tarjan算法 (以发现者Robert Tarjan命名)是一个在图中寻找强连通分量的算法。虽然发表时间更早,它仍可以被视为Kosaraju算法的一个改进。它的效率跟Gabow算法差不多。
此算法以一个有向图作为输入,并按照所在的强连通分量给出其顶点集的一个划分。图中的每个结点只在一个强连通分量中出现,即使是在有些结点单独构成一个强连通分量的情况下(比如图中出现了树形结构或孤立结点)。
Tarjan基于递归实现的深度优先搜索,在搜索过程中将顶点不断压入堆栈中,并在回溯时判断堆栈中顶点是否在同一联通分支。函数借助两个辅助数组pre和low,其中pre[u]为顶点u搜索的次序编号,low[u]为顶点u能回溯到的最早的顶点的次序编号。当pre[u]=low[u]时,则弹出栈中顶点并构成一个连通分支。Tarjan算法可以在线性时间内找出一个有向图的强分量,并且对原图进行一次DFS即可。
具体如下:任选一结点开始进行深度优先搜索(若深度优先搜索结束后仍有未访问的结点,则再从中任选一点再次进行)。搜索过程中已访问的结点不再访问。搜索树的若干子树构成了图的强连通分量。
结点按照被访问的顺序存入栈中。从搜索树的子树返回至一个结点时,检查该结点是否是某一强连通分量的根结点(见下)并将其从栈中删除。如果某结点是强连通分量的根,则在它之前出栈且还不属于其他强连通分量的结点构成了该结点所在的强连通分量。
根结点的性质
算法的关键在于如何判定某结点是否是强连通分量的根。注意“强连通分量的根”这一说法仅针对此算法,事实上强连通分量是没有特定的“根”的。在这里根结点指深度优先搜索时强连通分量中首个被访问的结点。
为找到根结点,我们给每个结点v一个深度优先搜索标号v.index,表示它是第几个被访问的结点。此外,每个结点v还有一个值v.lowlink,表示从v出发经有向边可到达的所有结点中最小的index。显然v.lowlink总是不大于v.index,且当从v出发经有向边不能到达其他结点时,这两个值相等。v.lowlink在深度优先搜索的过程中求得,v是强连通分量的根当且仅当v.lowlink = v.index。
Gabow算法
Gabow算法是Tarjan算法的提升版本,该算法类似于Tarjan算法。算法维护了一个顶点栈,但是还是用了第二个栈来确定何时从第一个栈中弹出各个强分支中的顶点,它的功能类似于Tarjan算法中的数组low。从起始顶点w处开始进行DFS过程中,当一条回路显示这组顶点都属于同一个强连通分支时,就会弹出栈二中顶点,只留下回边的目的顶点,也即搜索的起点w。
当回溯到递归起始顶点w时,如果此时该顶点在栈二顶部,则说明该顶点是一个强联通分量的起始顶点,那么在该顶点之后搜索的顶点都属于同一个强连通分支。于是,从第一个栈中弹出这些点,形成一个强连通分支。