基础-8:寻找强连通子图

1 概述

所谓强连通子图(一般是有向图),就是在图中存在某个子图SG,对于SG中的任意两个节点u,v,存在u -> … -> v的路径,也存在v-> … -> u的路径。强连通子图应用十分广泛,如社交网络中的社区发现(如亚文化人群是一个相对闭环)、洗钱环节的账户闭环等,寻找强连通子图是图计算的一个重要应用。

2 算法

发现强连通子图的著名算法包括Kosaraju算法Tarjan算法Gabow算法等,感兴趣的童鞋可以深入阅读。本文对寻找强连通子图的思路进行分析。

下面以一个例子分析寻找强连通子图的过程,如下图1所示:

《基础-8:寻找强连通子图》 图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所示:

《基础-8:寻找强连通子图》 图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算法也十分重要,在具体实践中,直接调用这些成熟的算法更为有效。

    原文作者:CodingTech
    原文地址: https://www.jianshu.com/p/4a9fac075b55
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞