强连通分量

xiaoxiao2021-02-28  76

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

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 u 2 compute transposition of G as G' 3 call DFS(G'),but in the main loop of DFS, consider the vertices 4 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 a 6 separate 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); } //求解强连通分量的第一次DFS public 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); } //求解强连通分量的第二次DFS public 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 

转载请注明原文地址: https://www.6miu.com/read-62732.html

最新回复(0)