1 概述
所谓强连通子图(一般是有向图),就是在图中存在某个子图SG,对于SG中的任意两个节点u,v,存在u -> ... -> v的路径,也存在v-> ... -> u的路径。强连通子图应用十分广泛,如社交网络中的社区发现(如亚文化人群是一个相对闭环)、洗钱环节的账户闭环等,寻找强连通子图是图计算的一个重要应用。
2 算法
发现强连通子图的著名算法包括Kosaraju算法、Tarjan算法、Gabow算法等,感兴趣的童鞋可以深入阅读。本文对寻找强连通子图的思路进行分析。
下面以一个例子分析寻找强连通子图的过程,如下图1所示:
图1:强连通子图示例
显然,(a,b,c,d)和(f,g,h,i)是两个强连通子图,如何找到这两个连通子图?肯定需要遍历图才可能发现。先看强连通子图的规律,对于(a,b,c,d),不妨取第一个访问的节点为a,当采用深度遍历方法时,当访问到c时,发现a是c的邻接节点,即边(c, a)是一条回边,继续深度遍历到d节点时,发现a是d的邻接节点,即(d, a)是一条回边,回边意味图中存在环,即存在强连通子图。
找到了强连通子图的规律,剩下的就是如何实现了。从上一段的分析看出,通过深度遍历的时候,可以发现回边,从而发现强连通子图。(深度遍历过程请详见前面的课程)深度遍历在第一次发现节点v时,将其涂灰,当深度遍历完v的所有邻接节点时,将v涂黑。这里的关键是在对v进行深度遍历时,如果发现v的子孙节点u,存在(u, v)这样的边,则存在回路;而且在深度遍历时,当一个节点遍历结束时,会回溯至其上层父节点root,继续深度遍历root的其它子节点。
这里,如果将深度遍历进行适度改造,则容易发现其中的强连通子图。基本思路:在深度遍历v时,同时建立一个栈,当遍历的过程中,根据邻接边,对相应的邻接节点依照遍历发现的顺序入栈,可以发现规律。下面以(a,b,c,d,f)这个顺序以此入栈,得到的结果如下图2所示:
图2: 栈示意图
图2中a'和a''示意存在回边。当深度遍历时,将所有的邻接节点都加入到栈中,如发现某个节点已被涂灰,则说明存在回边,将其与原节点区分,如图2中的a'和a''。当节点遍历完毕,回溯时,作出栈操作,如果出栈时,出现了<a'', ..., a>这样的序列(注:a''和a指向的都是同一个节点),则其中必然存在强连通子图,并且最大的<a'', ..., a>序列包含的元素才是寻找的强连通子图,如图2中(a,b,c,d)和(a, b, c)均是连通子图,但是(a,b,c,d)才是要求的强连通子图。
从上面的分析可以看出,寻找强连通子图就是深度遍历图过程中的入栈出栈,并且在出栈序列中寻找首位相连的序列。
上面的分析与算法导论书中讲解的求强连通子图,其本质是一样的,算法导论中利用的是转置图的再次遍历,与本文中借助栈实际上是一致的。个人认为,本文中提到的方法更容易理解。
具体的算法实现,作一个简单的概述:在深度遍历时要申请一个栈,当一个节点访问结束时,出栈并记录出栈的历史序列,如果存在首位相同的序列(可以借助hash记录),则记录此序列。遍历完毕后,如果存在强连通子图,必然会有一系列的强连通子图序列,从中寻找最大子序列,即为返回结果。
3 小结
虽然本文分析了寻找强连通子图的方法,但是,前文提及的三种算法:Kosaraju算法、Tarjan算法、Gabow算法也十分重要,在具体实践中,直接调用这些成熟的算法更为有效。
网友评论