强连通分量

来源:互联网 发布:手机电子琴乐器软件 编辑:程序博客网 时间:2024/06/05 07:09

强连通分量就是每个分量中的顶点都是两两都是含有路径可以互相达到的,强连通分量的用处不多,而且它也是相对于有向图来说的,无向图没有这一概念,它的作用是缩小图的规模,从而减小图的复杂度。


1、强连通分量概念:

图G是一个有向图,当且仅当每一对不同的顶点u,v,从u到v和v到u都有一条有向路径。


2、有向图分解为强连通分量算法

这个算法很巧妙,使用一次深度优先搜索标记每个节点的结束时间,将图转置,然后根据第一次深度优先搜索的结束时间从小到大顶点顺序再进行一次深度优先搜索得到一个森林,这个森林的每一棵树就是一个连通分量。下面是这个算法的伪代码:

STRONGLY-CONNECTED-COMPONENTS(G)1 call DFS(G) to compute finishing times u.f for each vertex u2 compute transposition of G as G'3 call DFS(G'),but in the main loop of DFS, consider the vertices4 in order of decreasing u.f (as computed in line 1)5 output the vertices of each tree in the depth-first forest in line 3 as a6separate strongly connected component


这个算法巧妙地运用了将图转置之后每条边反向,如果两个顶点之间可以互相到达,那么转置之前它们可以到达,转置之后它们仍可以到达,即它们在一棵树中,如果它们不可以互相到达,那么转置之后就不能到达,因此不在一棵树。例如左下图通过强连通分量分解后成为右边的有向图,通过分解缩小了图的大小。



3、java实现分解算法

算法使用的图的结构在另一篇文章数据结构 图的存储邻接矩阵与邻接链表中,如果需要可以点击查看。下面在实现算法中并没有真实的对第一次深度优先每个顶点的结束时间进行排序,因为仔细观察深度优先搜索可以发现如果将每个节点结束时的序列就是一个由小大的序列,因此只需要在第一次深度优先搜索每个节点结束时压入一个栈,那么第二次深度优先搜索从这个栈中取出节点就是按结束时间从小到大的一个节点序列。以下是算法的具体实现:

package Algorithm;/* * @vamesary * 4/2017 * 强连通分量Kusaraju算法 */import Structure.Graph;import Structure.LinkList;import Structure.Node;public class StrongConnect {private LinkList [] list;//邻接链表private int vertex=0;//图的顶点个数private int [] color;//是否已经经过private LinkList Stack;//强连通分支使用的栈private LinkList atree;//强连通分支的一棵树即一个分支private int time=0;public StrongConnect(int theVertex,int theEdge){Graph graph=new Graph(theVertex,theEdge,true);list=graph.getList();vertex=graph.getVertex();//System.out.println(graph.toString());}//使用现有图构造函数public  StrongConnect(Graph graph){list=graph.getList();vertex=graph.getVertex();//System.out.println(graph.toString());}//图的转置public void transposeGraph(){LinkList [] newlist=new LinkList[vertex];for(int i=0;i<vertex;i++)newlist[i]=new LinkList();for(int i=0;i<vertex;i++){Node p=list[i].getRoot();while(p!=null){newlist[p.id].insertOnStart(i, p.weight);p=p.next;}}list=newlist;}//强连通分量Kosaraju 算法public void Kosaraju(){time=0;color=new int[vertex];//颜色,代表该节点是否已被发现for(int i=0;i<vertex;i++){color[i]=0;//0代表White;1代表Gray;2代表Blank}Stack=new LinkList();for(int i=0;i<vertex;i++)if(color[i]==0)firstDFS(i);System.out.println();transposeGraph();//将图转置for(int i=0;i<vertex;i++){color[i]=0;}Node p=Stack.getRoot();while(p!=null){if(color[p.id]==0){if(atree!=null)System.out.println("得到一个强连通分支:"+atree);atree=new LinkList();secondDFS(p.id);}p=p.next;}System.out.println("得到一个强连通分支:"+atree);}//求解强连通分量的第一次DFSpublic void firstDFS(int id){color[id]=1;time=time+1;Node v=list[id].getRoot();while(v!=null){if(color[v.id]==0){firstDFS(v.id);}v=v.next;}time=time+1;//将"+id+"压入栈Stack.insertOnStart(id, time);System.out.println("节点: "+id+"  结束时间: "+time);}//求解强连通分量的第二次DFSpublic void secondDFS(int id){color[id]=1;atree.insertOnEnd(id, 0);Node v=list[id].getRoot();while(v!=null){if(color[v.id]==0){secondDFS(v.id);}v=v.next;}}}

测试:测试使用上图的有向图

节点: 7  结束时间: 7
节点: 5  结束时间: 8
节点: 3  结束时间: 9
节点: 1  结束时间: 10
节点: 6  结束时间: 13
节点: 4  结束时间: 14
节点: 2  结束时间: 15
节点: 0  结束时间: 16


得到一个强连通分支:链表的内容为:0 1 2 
得到一个强连通分支:链表的内容为:4 6 
得到一个强连通分支:链表的内容为:3 5 
得到一个强连通分支:链表的内容为:7 

0 0