有向图强连通分量tarjan算法

[有向图强连通分量]

在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。

//void tarjan(int u)

//{

// dfn[u]=low[u]=cnt++; //dfn[]还可以标记某点是否已搜索过

// Stack[top++]=u;

// in_sta[u]=true;

// for(int i=pre[u];i!=-1;i=edge[i].next){

// int v=edge[i].v;

// if(dfn[v]==-1){ //不要初始化dfn[…]=0,否则无法区分某个点是否已访问

// tarjan(v);

// if(low[u]>low[v]){ //后代节点的等效时间戳比当前节点要小,说明在一个环// low[u]=low[v];

// }

// }

// else if(in_sta[v]&&low[u]>dfn[v]){ //还留在栈中说明在一个环里

// low[u]=dfn[v];

// }

// }

// if(dfn[u]==low[u]){

// int j;

// while(true){

// j=Stack[--top];

// in_sta[j]=false;

// id[j]=scnt;

// if(j==u) break;

// }

// scnt++;

// }

//}

直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为O(N^2+M)。更好的方法是Kosaraju算法或Tarjan算法,两者的时间复杂度都是O(N+M)。本文介绍的是Tarjan算法。

[Tarjan算法]

Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。

定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。由定义可以得出,

?Low(u)=Min

{

DFN(u),

Low(v),(u,v)为树枝边,u为v的父节点

DFN(v),(u,v)为指向栈中节点的后向边(非横叉边)

tarjan(u)

{

DFN[u]=Low[u]=++Index // 为节点u设

定次序编号和Low初值

Stack.push(u)// 将节点u压入栈中

for each (u, v) in E // 枚举每一条边if(v is not visted)// 如果节点v未被访问过

tarjan(v)// 继续向下找

Low[u]= min(Low[u], Low[v])

else if(v in S)// 如果节点v还在栈内

Low[u]= min(Low[u], DFN[v])

if(DFN[u]== Low[u])// 如果节点u

是强连通分量的根

repeat

v = S.pop// 将v退栈,为该强连通分量中一个顶点

print v

until (u== v)

}

接下来是对算法流程的演示。

从节点1开始DFS,把遍历到的节点加入栈中。搜索到节点u=6时,DFN[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止,{6}为一个强连通分量。

返回节点5,发现DFN[5]=LOW[5],退栈后{5}为一个强连通分量。

返回节点3,继续搜索到节点4,把4加入堆栈。发现节点4向节点1有后向边,节点1还在栈中,所以LOW[4]=1。节点6已经出栈,(4,6)是横叉边,返回3,(3,4)为树枝边,所以LOW[3]=LOW[4]=1。

继续回到节点1,最后访问节点2。访问边(2,4),4还在栈中,所以LOW[2]=DFN[4]=5。返回1后,发现DFN[1]=LOW[1],把栈中节点全部取出,组成一个连通分量{1,3,4,2}。有向图是强连通图当且仅当图中存在经过每个顶点至少一次的回路

至此,算法结束。经过该算法,求出了图中全部的三个强连通分量{1,3,4,2},{5},{6}。可以发现,运行Tarjan算法的过程中,每个顶点都被访问了一次,且只进出了一次堆栈,每条边也只被访问了一次,所以该算法的时间复杂度为O(N+M)。

求有向图的强连通分量还有一个强有力的算法,为Kosaraju 算法。Kosaraju 是基于对有向图及其逆图两次DFS 的方法,其时间复杂度也是O(N+M)。与Trajan 算法相比,Kosaraju 算法可能会稍微更直观一些。但是Tarjan 只用对原图进行一次DFS ,不用建立逆图,更简洁。在实际的测试中,Tarjan 算法的运行效率也比Kosaraju 算法高30%左右。此外,该Tarjan 算法与求无向图的双连通分量(割点、桥)的Tarjan 算法也有着很深的联系。学习该Tarjan 算法,也有助于深入理解求双连通分量的Tarjan 算法,两者可以类比、组合理解。

求有向图的强连通分量的Tarjan 算法是以其发明者Robert Tarjan 命名的。Robert Tarjan 还发明了求双连通分量的Tarjan 算法,以及求最近公共祖先的离线Tarjan 算法,在此对Tarjan 表示崇高的敬意。 附:tarjan 算法的C++程序

?

[Copy to clipboard]View Code CPP

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

void tarjan (int i ) { int j ;

DFN [i ]=LOW [i ]=++Dindex ; instack [i ]=true ; Stap [++Stop ]=i ;

for (edge *e =V [i ];e ;e =e ->next ) { j =e ->t ; if (!DFN [j ]) { tarjan (j );

if (LOW [j ]

LOW [i ]=LOW [j ];

}

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

else if(instack[j]&& DFN[j]

LOW[i]=DFN[j];

}

if(DFN[i]==LOW[i])

{

Bcnt++;

do

{

j=Stap[Stop--];

instack[j]=false;

Belong[j]=Bcnt;

}

while(j!=i);

}

}

void solve()

{

int i;

Stop=Bcnt=Dindex=0;

memset(DFN,0,sizeof(DFN));

for(i=1;i<=N;i++)

if(!DFN[i])

tarjan(i);

}

[参考资料]

▪Wikipedia

▪Amber的图论总结

连通图的割点、割边(桥)、块、缩点,有向图的强连通分量

连通图的割点、割边(桥)、块、缩点,有向图的强连通分量 一、基本概念 无向图 割点:删掉它之后(删掉所有跟它相连的边),图必然会分裂成两个或两个以上的子图。 块:没有割点的连通子图 割边:删掉一条边后,图必然会分裂成两个或两个以上的子图,又称桥。 缩点:把没有割边的连通子图缩为一个点,此时满足任意两点间都有两条路径相互可达。 求块跟求缩点非常相似,很容易搞混,但本质上完全不同。割点可以存在多个块中(假如存在k个块中),最终该点与其他点形成k个块,对无割边的连通子图进行缩点后(假设为k个),新图便变为一棵k个点由k-1条割边连接成的树,倘若其中有一条边不是割边,则它必可与其他割边形成一个环,而能继续进行缩点。 有割点的图不一定有割边,如: 3是割点,分别与(1,2)和(4,5)形成两个无割点的块 有割边的图也不定有割点,如:

w(1,2)为割边, 有向图 强连通分量:有向图中任意两点相互可达的连通子图,其实也相当于无向图中的缩点 二、算法 无向图 借助两个辅助数组dfn[],low[]进行DFS便可找到无向图的割点和割边,用一个栈st[]维护记录块和“缩点”后连通子图中所有的点。 dfn[i]表示DFS过程中到达点i的时间,low[i]表示能通过其他边回到其祖先的最早时间。low[i]=min(low[i],dfn[son[i]]) 设 v,u之间有边w(v,u),从v->u: 如果low[u]>=dfn[v],说明v的儿子u不能通过其他边到达v的祖先,此时如果拿掉v,则必定把v的祖先和v的儿子u,及它的子孙分开,于是v便是一个割点,v和它的子孙形成一个块。 如果low[u]>dfn[v]时,则说明u不仅不能到达v的祖先,连v也不能通过另外一条边直接到达,从而它们之间的边w(v,u)便是割边,求割边的时候有一个重边的问题要视情况处理,如果v,u之间有两条无向边,需要仍视为割边的话,则在DFS的时候加一个变量记录它的父亲,下一步遇到父结点时不扩展回去,从而第二条无向重边不会被遍历而导致low[u]==dfn[v] ,而在另外一些问题中,比如电线连接两台设备A,B 如果它们之间有两根电线,则应该视为是双连通的,因为任何一条电线出问题都不会破坏A和B之间的连通性,这个时候,我们可以用一个used[]数组标记边的id,DFS时会把一条无向边拆成两条有向边进行遍历,但我们给它们俩同一个id号,在开始遍历v->u前检查它的id是否在上一次u->v 时被标记,这样如果两点之间有多条边时,每次遍历都只标记其中一条,还可以通过其他边回去,形成第二条新的路 求割点的时候,维护一个栈st 每遍历到一个顶点v则把它放进去,对它的子孙u如果dfn[u]为0,则表示还没有遍历到则先DFS(u),之后再判断low[u]和dfn[v],如果low[u]>=dfn[v],则把栈中从栈顶到v这一系列元素弹出,这些点与v 形成一个块,如果u的子孙x也是一个割点,这样做会不会错把它们和v,u放在一起形成一个块呢,这种情况是不会发生的,如果发现x是一个割点,则DFS 到x那一步后栈早就把属于x的子孙弹出来了,而只剩下v,u的子孙,它们之间不存在割点,否则在回溯到v之前也早就提前出栈了!画一个图照着代码模拟一下可以方便理解。 求割边也是一样的。

C语言技术中的图结构操作技巧

C语言技术中的图结构操作技巧 图结构是计算机科学中一种重要的数据结构,用于描述对象之间的关系。在C 语言中,我们可以使用一些技巧来操作图结构,使其更加高效和灵活。本文将介绍一些常用的图结构操作技巧,帮助读者更好地理解和应用图结构。 1. 图的表示方法 在C语言中,我们可以使用邻接矩阵或邻接表来表示图。邻接矩阵是一个二维数组,其中的元素表示两个顶点之间的边的关系。邻接表则是一个链表数组,每个链表表示一个顶点的邻接顶点。选择适合问题的表示方法可以提高图操作的效率。 2. 图的遍历 图的遍历是指按照一定的顺序访问图中的所有顶点。常用的图遍历算法有深度优先搜索(DFS)和广度优先搜索(BFS)。DFS使用递归或栈来实现,BFS使用队列来实现。遍历图可以帮助我们找到特定的顶点或路径,以及判断图的连通性等问题。 3. 最小生成树 最小生成树是指在一个连通图中选择一些边构成的子图,使得这些边的权值之和最小,并且包含图中的所有顶点。常用的最小生成树算法有Prim算法和Kruskal 算法。Prim算法从一个顶点开始,逐渐扩展生成树,直到包含所有顶点。Kruskal 算法则是按照边的权值从小到大选择边,直到生成树中包含所有顶点。 4. 最短路径 最短路径是指两个顶点之间的路径中,边的权值之和最小的路径。常用的最短路径算法有Dijkstra算法和Floyd-Warshall算法。Dijkstra算法适用于单源最短路径问题,通过维护一个距离数组来记录每个顶点到起始顶点的最短路径。Floyd-

Warshall算法适用于多源最短路径问题,通过动态规划的方式计算任意两个顶点之 间的最短路径。 5. 拓扑排序 拓扑排序是对有向无环图进行排序的一种方法,使得对于任意一条有向边(u, v),顶点u在排序中出现在顶点v之前。拓扑排序可以用来解决任务调度等问题。常用的拓扑排序算法有深度优先搜索和广度优先搜索。在拓扑排序过程中,我们可以使用一个入度数组来记录每个顶点的入度,从而确定拓扑排序的顺序。 6. 强连通分量 强连通分量是指有向图中的一个子图,其中任意两个顶点之间都存在双向路径。强连通分量可以帮助我们分析图的结构和性质。常用的强连通分量算法有Tarjan 算法和Kosaraju算法。Tarjan算法使用深度优先搜索来寻找强连通分量,Kosaraju 算法则使用两次深度优先搜索来寻找强连通分量。 7. 最小割 最小割是指将图分割成两个不相交的子图,使得两个子图之间的边的权值之和 最小。最小割可以用于网络流问题等领域。常用的最小割算法有Ford-Fulkerson算 法和Edmonds-Karp算法。这些算法通过不断寻找增广路径来找到最小割。 总结: 图结构在计算机科学中具有广泛的应用。通过掌握图的表示方法、遍历算法、 最小生成树、最短路径、拓扑排序、强连通分量和最小割等技巧,我们可以更好地处理和解决与图相关的问题。在实际应用中,我们需要根据具体问题的特点选择合适的图操作技巧,以提高程序的效率和性能。

求强连通分量的Kosaraju算法和Tarjan算法的比较 by ljq

求强连通分量的Kosaraju算法和Tarjan算法的比较 一、定义 在有向图中,如果两个顶点vi,vj间有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图的每两个顶点都强连通,则称该有向图是一个强连通图。非强连通的有向图的极大强连通子图,称为强连通分量(strongly connected components)。 而对于一个无向图,讨论强连通没有意义,因为在无向图中连通就相当于强连通。 由一个强连通分量内的所有点所组成的集合称为缩点。在有向图中的所有缩点和所有缩点之间的边所组成的集合称为该有向图的缩图。 例子: 原图: 缩图: 上面的缩图中的 缩点1包含:1、2,;缩点2包含:3; 缩点3包含:4;缩点4包含:5、6、7。

二、求强连通分量的作用 把有向图中具有相同性质的点找出来,形成一个集合(缩点),建立缩图,能够方便地进行其它操作,而且时间效率会大大地提高,原先对多个点的操作可以简化为对它们所属的缩点的操作。 求强连通分量常常用于求拓扑排序之前,因为原图往往有环,无法进行拓扑排序,而求强连通分量后所建立的缩图则是有向无环图,方便进行拓扑排序。 三、Kosaraju算法 时间复杂度:O(M+N)注:M代表边数,N代表顶点数。 所需的数据结构:原图、反向图(若在原图中存在vi到vj的有向边,在反向图中就变成为vj到vi的有向边)、标记数组(标记是否遍历过)、一个栈(或记录顶点离开时间的数组)。 算法描叙: 步骤1:对原图进行深度优先遍历,记录每个顶点的离开时间。 步骤2:选择具有最晚离开时间的顶点,对反向图进行深度优先遍历,并标记能够遍历到的顶点,这些顶点构成一个强连通分量。 步骤3:如果还有顶点没有遍历过,则继续进行步骤2,否则算法结束。 hdu1269(Kosaraju算法)代码: #include #include const int M=10005; struct node { int vex; node *next; }; node *edge1[M],*edge2[M]; bool mark1[M],mark2[M]; int T[M],Tcnt,Bcnt; void DFS1(int x)

算法学习:图论之图的割点,桥,双连通分支

图的割点、桥与双连通分支 [点连通度与边连通度] 在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成多个连通块,就称这个点集为割点集合。一个图的点连通度的定义为,最小割点集合中的顶点数。 类似的,如果有一个边集合,删除这个边集合以后,原图变成多个连通块,就称这个点集为割边集合。一个图的边连通度的定义为,最小割边集合中的边数。 注:以上定义的意思是,即有可能删除两个或两个以上点的时候才能形成多个连通块! [双连通图、割点与桥] 如果一个无向连通图的点连通度大于1,则称该图是点双连通的(point biconnected),简称双连通或重连通。一个图有割点,当且仅当这个图的点连通度为1,则割点集合的唯一元素被称为割点(cut point),又叫关节点(articulation point)。 如果一个无向连通图的边连通度大于1,则称该图是边双连通的(edge biconnected),简称双连通或重连通。一个图有桥,当且仅当这个图的边连通度为1,则割边集合的唯一元素被称为桥(bridge),又叫关节边(articulation edge)。 可以看出,点双连通与边双连通都可以简称为双连通,它们之间是有着某种联系的,下文中提到的双连通,均既可指点双连通,又可指边双连通。 [双连通分支] 在图G的所有子图G’中,如果G’是双连通的,则称G’为双连通子图。如果一个双连通子图G’它不是任何一个双连通子图的真子集,则G’为极大双连通子图。双连通分支(biconnected component),或重连通分支,就是图的极大双连通子图。特殊的,点双连通分支又叫做块。

强连通分量与模拟链表

强联通分量与模拟链表 作者:逸水之寒 1.强连通分量 强连通分量的定义是:在有向图中,u可以到达v,但是v不一定能到达u,如果u,v 到达,则他们就属于一个强连通分量。 求强连通分量最长用的方法就是Kosaraju算法,比较容易理解而且效率很高,本文对强连通分量的求法均采用Kosaraju算法。 其主要思想:首先对原图G进行深搜形成森林(树),然后选择一棵树进行第二次深搜,注意第一次是要判断节点A能不能通向节点B,而第二次要判断的是节点B能不能通向A,能遍历到的就是一个强连通分量。(附录给出伪代码) Kosaraju算法如果采用了合适的数据结构,它的时间复杂度是O(n)的。相关题目有很多,例如USACO 5.3.3,2009NOIP Senior No.3。下面将以USACO 5.3.3 schlnet 举例说明。 Preblem 1. Network of Schools (USACO 5.3.3 schlnet\IOI96 No.3) A number of schools are connected to a computer network. Agreements have been developed among those schools: each school maintains a list of schools to which it distributes software (the "receiving schools"). Note that if B is in the distribution list of school A, then A does not necessarily appear in the list of school B. You are to write a program that computes the minimal number of schools that must receive a copy of the new software in order for the software to reach all schools in the network according to the agreement (Subtask A). As a further task, we want to ensure that by sending the copy of new software to an arbitrary school, this software will reach all schools in the network. To achieve this goal we may have to extend the lists of receivers by new members. Compute the minimal number of extensions that have to be made so that whatever school we send the new software to, it will reach all other schools (Subtask B). One extension means introducing one new member into the list of receivers of one school.

强连通分量个数的最小值

强连通分量个数的最小值 1. 引言 在图论中,强连通分量是指图中的一组顶点,其中任意两个顶点都存在一条有向路径。强连通分量个数的最小值是指在一个有向图中,最少需要将多少个顶点组成一个强连通分量。本文将介绍强连通分量的概念、计算方法以及如何求解强连通分量个数的最小值。 2. 强连通分量的定义 在有向图中,如果从顶点A到顶点B存在一条有向路径,同时从顶点B到顶点A也存在一条有向路径,则称顶点A和顶点B是强连通的。如果一个有向图中的每个顶点都与其他所有顶点强连通,则该有向图被称为强连通图。而强连通分量则是指有向图中的一组顶点,其中任意两个顶点都是强连通的,且不与其他顶点强连通。 3. 强连通分量的计算方法 为了计算一个有向图的强连通分量,可以使用强连通分量算法,其中最常用的是Tarjan算法和Kosaraju算法。 3.1 Tarjan算法 Tarjan算法是一种深度优先搜索算法,用于寻找有向图的强连通分量。算法的基本思想是通过DFS遍历图中的每个顶点,并记录每个顶点的遍历次序和能够到达的最小顶点次序。通过这些信息,可以判断顶点是否属于同一个强连通分量。 具体步骤如下: 1.初始化一个空栈和一个空的遍历次序数组。 2.对于每个未遍历的顶点,进行深度优先搜索。 3.搜索过程中,记录每个顶点的遍历次序和能够到达的最小顶点次序,并将顶 点加入栈中。 4.当搜索完成后,根据遍历次序和能够到达的最小顶点次序,可以确定每个顶 点所属的强连通分量。 3.2 Kosaraju算法 Kosaraju算法是另一种用于计算有向图强连通分量的算法。算法的基本思想是通过两次深度优先搜索来确定强连通分量。 具体步骤如下: 1.对原始图进行一次深度优先搜索,记录顶点的遍历次序。

tarjan算法的原理

tarjan算法的原理 Tarjan算法原理及应用 一、引言 Tarjan算法是一种用于图的深度优先搜索的算法,它可以在无向图或有向图中找到所有强连通分量。这个算法由美国计算机科学家Robert Tarjan于1972年提出,被广泛应用于图论和算法领域。本文将介绍Tarjan算法的原理及其应用。 二、Tarjan算法原理 1. 深度优先搜索 Tarjan算法是基于深度优先搜索的,深度优先搜索是一种图遍历算法,从一个顶点出发,沿着一条路径一直往下走,直到不能再走为止,然后回溯到前一个顶点,继续向未走过的路径探索。这种搜索方式可以用递归或栈来实现。 2. 强连通分量 在图中,如果任意两个顶点之间都存在路径,那么它们构成一个强连通分量。强连通分量是图中的一个重要概念,它可以帮助我们理解图结构的特性。 3. Tarjan算法步骤 Tarjan算法通过深度优先搜索来寻找强连通分量,其具体步骤如下:(1)初始化。将所有顶点标记为未访问状态,定义一个栈来保存已

经访问的顶点。 (2)深度优先搜索。从图中的任意一个未访问的顶点开始进行深度优先搜索。 (3)标记顶点。在搜索过程中,对每个顶点进行标记,记录其访问顺序(也称为时间戳)和能够到达的最小时间戳。 (4)寻找强连通分量。当一个顶点的访问顺序等于能够到达的最小时间戳时,说明它是一个强连通分量的根节点。通过弹出栈中的顶点,可以找到该强连通分量中的所有顶点。 三、Tarjan算法应用 Tarjan算法在图论和算法设计中有着广泛的应用,下面介绍几个常见的应用场景: 1. 强连通分量的查找 Tarjan算法可以高效地找到图中的所有强连通分量。这对于解决一些实际问题非常有用,比如社交网络中的群组划分、电路中的等价关系判断等。 2. 有向图的可达性分析 在有向图中,Tarjan算法可以用来判断两个顶点之间是否存在路径。这对于解决一些路径相关的问题非常有帮助,比如寻找关键路径、判断死锁等。 3. 编译器优化

Gabow算法

有向图强连通分量的定义:在有向图G中,如果两个顶点vi,vj间(vi != vj)有一条从vi到vj的有向路径,同时还有一条从vj到vi的有向路径,则称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。 对于一幅无向图来说,只要2个顶点相连在同一路径上,那么这两个顶点就是连通的,从其中一个顶点可以到达另一顶点,求出哪些顶点是连通的也并不难。而对于有向图,受方向限制,两个连通的顶点并不一定能互相到达。如果两个连通的顶点可以相互到达,那么这两个顶点是强连通的。如果将图中所有的互相强连通的顶点划为一组,那么这组顶点就是强连通分量。高效求出强连通分量的算法有Kosaraju算法,Tarjan算法,Gabow算法。我表示我只能看懂Gabow算法。 据说Gabow算法和Tarjan算法的思想是一样的。每个强连通分量都有一个“根”,根是强连通分量中最先被检查的顶点。在一组强连通分量中,沿着根不断深度优先搜索,最终将会回到根,路上经过的顶点则属于这个强连通分量。 准备工作: 因为是演示,假定只有6个顶点,而且序号是连续的。#define MAXVERTEX 6 首先需要一个数组,用来保存当前顶点被检测的顺序。int Order[MAXVERTEX];在这里将-1定为初始值,即没有被检测。Order[v]表示顶点v是第几个被检测的。除了这个数组,还需要一个变量,用来表示当前的顺序。int OrderNum=0;初始化为0表示还没开始检测。 结果也以数组的形式表示,为每个强连通分量分配一个不同的编号,而一个强连通分量中的所有顶点编号相同。int Part[MAXVERTEX];开始这个数组应该被初始化为-1,表示还不确定其中的顶点属于哪个强连通分量。同样还需要一个变量表示当前处理到第几个强连通分量。int PartNum=0;表示还没开始处理。 另外还需要2个辅助栈。第一个用来保存在一次深度优先搜索过程中遇到的顶点。stack Path;这些顶点还没有被确定属于哪个强连通分量,在确定顶点属于某个强连通分量之后,就记录结果,将顶点弹出堆栈。第二个用来保存在一次深度优先搜索过程中遇到的根。stack Root; 步骤1: 在所有顶点中,找一个没有被访问过的节点v,以v为参数执行步骤2。否则完成。 步骤2: 记录v的访问顺序。 将v压入堆栈Path和Root。 如果v指向其它的邻接顶点,那么对于每个邻接顶点next: 1) 如果没有访问过,则以next为参数,递归执行步骤2。 2) 如果访问过,而且没有确定它属于哪个强连通分量,弹出Root栈中next之后所有的顶点。 如果Root栈的顶元素等于v,那么在Part中记录顶点对应的的强连通分量。 最后递归返回。 代码: #include #include

图的连通性检测方法

图的连通性检测方法 图论是数学的一个分支,研究图形结构以及图形之间的关系。在图 论中,连通性是一个重要的概念,用于描述图中的节点或顶点之间是 否存在路径相连。连通性检测方法是用来确定一个图是否是连通图的 方法。本文将介绍几种常用的图的连通性检测方法。 一、深度优先搜索(DFS) 深度优先搜索是一种常用的图遍历算法,也可以用来检测图的连通性。该方法从图中的一个顶点开始,沿着一条路径尽可能深的搜索, 直到到达无法继续搜索的节点,然后回溯到上一个节点,继续搜索其 他路径。具体步骤如下: 1. 选择一个起始节点作为根节点。 2. 遍历该节点的邻接节点,并标记为已访问。 3. 递归的访问未访问过的邻接节点,直到所有节点都被访问过。 4. 如果所有节点都被访问过,则图是连通的;否则,图是不连通的。 DFS算法的时间复杂度为O(V+E),其中V是节点数,E是边数。 二、广度优先搜索(BFS) 广度优先搜索也是一种常用的图遍历算法,同样可以用来检测图的 连通性。该方法从图中的一个顶点开始,先访问其所有邻接节点,然 后再依次访问它们的邻接节点。具体步骤如下:

1. 选择一个起始节点作为根节点。 2. 将该节点加入一个队列中。 3. 从队列中取出一个节点,并标记为已访问。 4. 遍历该节点的邻接节点,将未访问过的节点加入队列中。 5. 重复步骤3和步骤4,直到队列为空。 6. 如果所有节点都被访问过,则图是连通的;否则,图是不连通的。 BFS算法的时间复杂度同样为O(V+E)。 三、并查集 并查集是一种数据结构,常用于解决图的连通性问题。它可以高效 地合并集合和判断元素是否属于同一个集合。具体步骤如下: 1. 初始化并查集,每个节点都是一个独立的集合。 2. 遍历图中的每条边,将边的两个节点合并到同一个集合中。 3. 判断图是否连通的方法是查找两个节点是否属于同一个集合。 并查集的时间复杂度为O(V+E)。 四、最小生成树 最小生成树是指一个连通图的生成树,其所有边的权值之和最小。 如果一个图是连通的,那么它的最小生成树就是它本身。因此,可以

Tarjan算法

[有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。 下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。 直接根据定义,用双向遍历取交集的方法求强连通分量,时间复杂度为 O(N^2+M)。更好的方法是Kosaraju算法或Tarjan算法,两者的时间复杂度都是O(N+M)。本文介绍的是Tarjan算法。 [Tarjan算法] Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。 定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的最早的栈中节点的次序号。由定义可以得出, Low(u)=Min{DFN(u),Low(v),(u,v)为树枝边,u为v的父节点 DFN(v),(u,v)为指向栈中节点的后向边(非横叉边)} 当DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。 算法伪代码如下

tarjan(u) { DFN[u]=Low[u]=++Index // 为节点u设定次序编号和Low初值 Stack.push(u)// 将节点u压入栈中 for each (u, v) in E // 枚举每一条边 if(v is not visted)// 如果节点v未被访问过 tarjan(v)// 继续向下找 Low[u]= min(Low[u], Low[v]) else if(v in S)// 如果节点u还在栈内 Low[u]= min(Low[u], DFN[v]) if(DFN[u]== Low[u])// 如果节点u是强连通分量的根 repeat v = S.pop// 将v退栈,为该强连通分量中一个顶点 print v until (u== v) } 接下来是对算法流程的演示。 从节点1开始DFS,把遍历到的节点加入栈中。搜索到节点u=6时, DFN[6]=LOW[6],找到了一个强连通分量。退栈到u=v为止,{6}为一个强连通分量。 返回节点5,发现DFN[5]=LOW[5],退栈后{5}为一个强连通分量。

tarjan LCA 算法

tarjan LCA 算法 2009-03-26 02:14 LCA是求最近公共祖先问题, tarjan的算法是离线算法,时间复杂度为O(n+Q),n为数据规模,Q为询问个数 其中用到并查集。关键是dfs的主循环比较重要。离线算法就是对每个查询,都要求以下,此算法在lrj的黑书中简单提起过,后边还有O(n)-o(1)的算法,正在研究中。。。 分类,使每个结点都落到某个类中,到时候只要执行集合查询,就可以知道结点的LCA了。 对于一个结点u,类别有以u为根的子树、除类一以外的以f(u)为根的子树、除前两类以外的以f(f(u))为根的子树、除前三类以外的以f(f(f(u)))为根的子树…… 类一的LCA为u,类二为f(u),类三为f(f(u)),类四为f(f(f(u)))。这样的分类看起来好像并不困难。但关键是查询是二维的,并没有一个确定的u。接下来就是这个算法的巧妙之处了。 利用递归的LCA过程。当lca(u)执行完毕后,以u为根的子树已经全部并为了一个集合。而一个lca的内部实际上做了的事就是对其子结点,依此调用lca.当v1(第一个子结点)被lca,正在处理v2的时候,以v1为根的子树+u同在一个集合里,f(u)+编号比u小的u的兄弟的子树同在一个集合里,f(f(u)) + 编号比f(u)小的f(u)的兄弟的子树同在一个集合里……而这些集合,对于v2的LCA都是不同的。因此只要查询x在哪一个集合里,就能知道 LCA(v2,x) 还有一种可能,x不在任何集合里。当他是v2的儿子,v3,v4等子树或编号比u大的u的兄弟的子树(等等)时,就会发生这种情况。即还没有被处理。还没有处理过的怎么办?把一个查询(x1,x2)往查询列表里添加两次,一次添加到x1的列表里,一次添加到x2的列表里,如果在做x1的时候发现 x2已经被处理了,那就接受这个询问。(两次中必定只有一次询问被接受) 其他介绍: 首先,Tarjan算法是一种离线算法,也就是说,它要首先读入所有的询问(求一次LCA叫做一次询问),然后并不一定按照原来的顺序处理这些询问。而打乱这个顺序正是这个算法的巧妙之处。看完下文,你便会发现,如果偏要按原来的顺序处理询问,Tarjan算法将无法进行。Tarjan算法是利用并查集来实现的。它按DFS的顺序遍历整棵树。对于每个结点x,它进行以下几步操作: * 计算当前结点的层号lv[x],并在并查集中建立仅包含x结点的集合,即 root[x]:=x。 * 依次处理与该结点关联的询问。 * 递归处理x的所有孩子。 * root[x]:=root[father[x]](对于根结点来说,它的父结点可以任选一个,反正这是最后一步操作了)。

强连通分量的三种算法

有向图中, u可达v不一定意味着v可达u. 相互可达则属于同一个强连通分量(S trongly Connected Component, SCC) 最关键通用部分:强连通分量一定是图的深搜树的一个子树。 一、Kosaraju算法 1. 算法思路 基本思路: 这个算法可以说是最容易理解,最通用的算法,其比较关键的部分是同时应用了原图G和反图GT。(步骤1)先用对原图G进行深搜形成森林(树),(步骤2)然后任选一棵树对其进行深搜(注意这次深搜节点A能往子节点B走的要求是EAB存在于反图GT),能遍历到的顶点就是一个强连通分量。余下部分和原来的森林一起组成一个新的森林,继续步骤2直到没有顶点为止。 改进思路: 当然,基本思路实现起来是比较麻烦的(因为步骤2每次对一棵树进行深搜时,可能深搜到其他树上去,这是不允许的,强连通分量只能存在单棵树中(由开篇第一句话可知)),我们当然不这么做,我们可以巧妙的选择第二深搜选择的树的顺序,使其不可能深搜到其他树上去。想象一下,如果步骤2是从森林里选择树,那么哪个树是不连通(对于GT来说)到其他树上的呢?就是最后遍历出来的树,它的根节点在步骤1的遍历中离开时间最晚,而且可知它也是该树中离开时间最晚的那个节点。这给我们提供了很好的选择,在第一次深搜遍历时,记录时间i离开的顶点j,即numb[i] =j。那么,我们每次只需找到没有找过的顶点中具有最晚离开时间的顶点直接深搜(对于GT来说)就可以了。每次深搜都得到一个强连通分量。 隐藏性质: 分析到这里,我们已经知道怎么求强连通分量了。但是,大家有没有注意到我们在第二次深搜选择树的顺序有一个特点呢?如果在看上述思路的时候,你的脑子在思考,相信你已经知道了!!!它就是:如果我们把求出来的每个强连通分量收缩成一个点,并且用求出每个强连通分量的顺序来标记收缩后的节点,那么这个顺序其实就是强连通分量收缩成点后形成的有向无环图的拓扑序列。为什么呢?首先,应该明确搜索后的图一定是有向无环图呢?废话,如果还有环,那么环上的顶点对应的所有原来图上的顶点构成一个强连通分量,而不是构成环上那么多点对应的独自的强连通分量了。然后就是为什么是拓扑序列,我们在改进分析的时候,不是先选的树不会连通到其他树上(对于反图GT来说),也就是后选的树没有连通到先选的树,也即先出现的强连通分量收缩的点只能指向后出现的强连通分量收缩的点。那么拓扑序列不是理所当然的吗?这就是Kosaraju算法的一个隐藏性质。 2. 伪代码 Kosaraju_Algorithm: step1:对原图G进行深度优先遍历,记录每个节点的离开时间。

数据结构课设有向图强连通分量求解

数据结构课设有向图强连通分量求解 强连通分量(Strongly Connected Components)是有向图中的一种特殊的连通分量,指的是图中任意两个顶点之间都存在有向路径。 求解有向图的强连通分量可以使用Tarjan算法,该算法基于深度优先搜索(DFS)。 具体步骤如下: 1. 初始化一个栈,用于存储已访问的顶点; 2. 对于图中的每个顶点v,如果v未访问,则进行DFS遍历; 3. 在DFS遍历中,对于当前访问的顶点v,设置v的索引号和低链接号为当前索引值,并将v入栈; 4. 遍历v的所有邻接顶点w,如果w未访问,则进行DFS遍历,并将w的低链接号更新为min(当前顶点的低链接号, w的低链接号); 5. 如果当前顶点v的低链接号等于索引号,则将v出栈,并将v及其出栈的顶点组成一个强连通分量。 示例代码如下: ```python class Graph: def __init__(self, vertices): self.V = vertices self.adj = [[] for _ in range(vertices)] self.index = 0

self.lowlink = [float("inf")] * vertices self.onStack = [False] * vertices self.stack = [] self.scc = [] def addEdge(self, u, v): self.adj[u].append(v) def tarjanSCC(self, v): self.index += 1 self.lowlink[v] = self.index self.stack.append(v) self.onStack[v] = True for w in self.adj[v]: if self.lowlink[w] == float("inf"): self.tarjanSCC(w) self.lowlink[v] = min(self.lowlink[v], self.lowlink[w]) elif self.onStack[w]: self.lowlink[v] = min(self.lowlink[v], self.lowlink[w]) if self.lowlink[v] == self.index: scc = [] w = -1 while w != v:

有向图的强连通分量

For personal use only in study and research; not for commercial use 强连通分量 一个有向图中,如果节点i能够通过一些边到达节点j,就简写成i能到达j。如果对于任意两个节点i,j均有i能到达j或j能到达i,则说此图是连通的。如果对于任意两个节点i,j均有i能到达j且j能到达i,则说此图是强连通的。 对于一个无向图,说强联通没有意义,因为此时强连通就是连通。而对于一个有向图,它不一定是强连通的,但可以分为几个极大的强连通子图(“极大”的意思是再加入任何一个顶点就不满足强连通了)。这些子图叫做这个有向图的强连通分量。在上图中,强连通分量是A{1},B{2,4},C{3,5,6,7}。 在一个强连通分量中的节点由于有着相似的拓扑性质,所以我们可以将其紧缩为一个节点(这让我想到了什么?化学里的“族”),于是就大大减小了图的规模。比如上图紧缩为A,B,C 三个节点后,整个图就成为了B←A→C的简单形式。所以强连通分量是一个很有意义的东西。然而如果根据定义,对每个节点进行一次O(n2)的DFS以求出它所能到达的顶点的话,整个算法的时间复杂度就是迟钝的O(n3)。下面将介绍两种用O(n2)求强连通分量的算法:Kosaraju算法和Tarjan算法。 Kosaraju算法 Kosaraju算法基于以下思想:强连通分量一定是某种DFS形成的DFS树森林。Kosaraju 算法给出了更具体的方式: ①任意进行一次DFS,记录下每个节点的结束时间戳f[i]。 ②按f[i]的大小对节点进行排序(从小到大)。 ③以②的排序结果为顺序再进行一次DFS,所得的DFS树森林即为强连通分量。这次DFS 可以用Floodfill进行,把每个强连通分量标上不同序号。 (这就是OI界传说中的“求强连两遍DFS的算法”) 比如上图,我们可以得到: d[1]=1 f[1]=14 d[2]=2 f[2]=5 d[3]=6 f[3]=13 d[4]=3 f[4]=4 d[5]=7 f[5]=8 d[6]=9 f[6]=12 d[7]=10 f[7]=11 根据f[i]排序得:④②⑤⑦⑥③①(发现了什么?就是后序遍历),再按照这个顺序进行DFS即可。 程序: var a:array[1..1000,1..1000]of longint; b,flag:array[1..1000]of longint; n,i,j,m,k:longint; procedure dfs(x:longint); var j:longint; begin

找基本割集的简单方法

找基本割集的简单方法 一、背景介绍 基本割集是图论中的一个重要概念,它是指在一个连通图中,删去某个边或节点后使得原来的图不再连通的最小集合。找到基本割集可以帮助我们更好地理解图的结构和性质,因此在实际应用中具有广泛的应用价值。 二、定义及性质 1. 定义:在一个连通图G=(V,E)中,如果删去某个边或节点后使得原来的图不再连通,则这个边或节点被称为该图的割点或割边;如果这个割点或割边所组成的集合是该图不同联通分量之间唯一的,则称这个集合为该图的基本割集。 2. 性质: (1)每个基本割集都至少包含一个割点或者一条割边; (2)对于任意两个不同联通分量之间只有唯一一条路径; (3)将任意一个基本割集划分成两部分,则这两部分所对应的子图均为联通图。 三、找基本割集方法 1. 基于DFS算法 深度优先搜索算法(DFS)可以遍历整张连通图,并根据遍历顺序来

确定每个节点的遍历顺序。在DFS遍历的过程中,如果我们发现某个节点的子节点不再与该节点相连,则说明该节点是一个割点,而该节点所连接的两个子图就是一个基本割集。具体步骤如下: (1)从任意一个节点开始进行DFS遍历; (2)记录每个节点的遍历顺序和最早访问时间; (3)对于每个非根节点v,如果存在一个子节点w,满足 dfn[w]=depth[v],则说明v是一个割点; (4)对于每个连通分量,将其所有割点和相应子图组成的集合作为一个基本割集。 3. 基于Tarjan算法 Tarjan算法是一种高效的寻找强连通分量的算法,在寻找强连通分量的过程中可以顺带找到基本割集。具体步骤如下: (1)从任意一个节点开始进行Tarjan算法;

算法导论第十五章习题答案

算法导论第十五章习题答案 算法导论第十五章习题答案 算法导论是一本经典的计算机科学教材,其中第十五章涵盖了图算法的内容。 本文将针对该章节中的习题进行解答,并对其中一些问题进行深入探讨。 1. 习题15.1-1 题目要求证明:对于任意的有向图G=(V,E),如果图中不存在从节点u到节点v 的路径,则在每个强连通分量中,节点u和节点v都在同一个强连通分量中。 证明:假设存在一个强连通分量C,其中节点u在C中,节点v在C'中(C'为 除了C之外的其他强连通分量)。由于不存在从u到v的路径,所以在C中不 存在从u到v的路径。但是根据强连通分量的定义,C中的任意两个节点之间 都存在路径。所以存在一条从v到u的路径。这与C'中的节点v不在C中矛盾,所以假设不成立,节点u和节点v必定在同一个强连通分量中。 2. 习题15.2-2 题目要求证明:在一个有向无环图中,存在一个拓扑排序,使得任意两个非根 节点u和v,u在v之前。 证明:假设存在一个有向无环图G=(V,E),不存在上述所要求的拓扑排序。即对于任意的拓扑排序,存在两个非根节点u和v,u在v之后。那么我们可以得到一条从u到v的路径。由于图中不存在环,所以路径上的节点不会重复。我们 可以将路径上的节点按照拓扑排序的顺序排列,得到一个新的拓扑排序,使得 u在v之前。这与假设矛盾,所以原命题成立。 3. 习题15.3-3 题目要求证明:在一个有向图G=(V,E)中,如果存在一条从节点u到节点v的路

径,那么在图的转置G^T中,存在一条从节点v到节点u的路径。 证明:假设存在一条从节点u到节点v的路径。那么在图的转置G^T中,边(u,v)变成了边(v,u)。所以存在一条从节点v到节点u的路径。因此,原命题成立。 4. 习题1 5.4-1 题目要求:给出一个算法,判断一个有向图G=(V,E)是否是有向无环图。 算法思路:我们可以使用深度优先搜索(DFS)来判断是否存在环。具体步骤如下: 1. 对于图中的每个节点v,设置一个状态标记visited[v]为false。 2. 对于图中的每个节点v,如果visited[v]为false,则调用DFS-Visit(v)。 3. 在DFS-Visit(v)中,首先将visited[v]设为true,然后对于节点v的每个邻接节点u,如果visited[u]为false,则递归调用DFS-Visit(u)。 4. 如果在DFS-Visit(v)的过程中,发现某个邻接节点u已经被访问过(visited[u]为true),则说明存在环,返回false。 5. 如果所有节点都被访问过且没有发现环,则返回true。 5. 习题15-5 题目要求:给出一个算法,找到有向图G=(V,E)中的所有强连通分量。 算法思路:我们可以使用Kosaraju算法来找到有向图中的所有强连通分量。具体步骤如下: 1. 对于图G,首先进行一次深度优先搜索(DFS),得到图G的拓扑排序。 2. 对图G进行转置,得到图G^T。 3. 对图G^T按照拓扑排序的逆序进行深度优先搜索(DFS)。

(精选)北大PKU 慕课 EDX 数据结构与算法 第八章图 quiz答案与解析

第八章图 PROBLEM 1 (1/1 分) 下图中的强连通分量的个数为多少个? How many strongly connected graphs in the under graph? 3 3 - 正确 3 3 Explanation 有向图强连通的极大子图称为该有向图的强连通分支或者强连通分量。分别为最左边1个点组成的极大子图,中间4个点组成的极大子图和最右边1个点组成的极大子图。分别为最左边1个点,中间4个点和最右边1个点。 Maximal strongly connected subgraphs of a directed graph are called strongly connected components of this directed graph.They are the subgraph consist of the left-most vertex, the subgraph consist of 4 vertices in the middle, ,and the subgraph consist of the right-most vertex respectively.

PROBLEM 2 (1/1 分) 下面关于图的说法正确的有 (多选) The right statements of graphs in the following are: (There are more than one correct answers) 对于无向图,所有结点的度数加起来一定是偶数。 As for undirected graphs, the sum of degrees of all vertices is definitely even number., 将有向图的一个强连通分量中的边全部反向仍然是强连通分量。Reversion all the edges of a strongly connected component of a directed graph, then the subgraph is still a strongly connected component., - 正确 对于无向图,所有结点的度数加起来一定是偶数。 As for undirected graphs, the sum of degrees of all vertices is definitely even number. 将有向图的一个强连通分量中的边全部反向仍然是强连通分量。Reversion all the edges of a strongly connected component of a directed graph, then the subgraph is still a strongly connected component. 对于有向图,每个结点的出度必须要等于入度。As for directed graph, each vertices’ out-degree is equal to its in-degree. 对于一个连通图,一定存在一种给边添加方向的方案使得这个图变成强连通图。For a connected graph, there must be a way of directing all the edges of the original graph to make the graph strongly connected graph. Explanation 结点度数是边数的2倍,故一定为偶数。 The sum of degrees of vertices is equal to the amount of edge times 2, so it must be even number. 原来强连通分量中的点必须能够互达,边全部反向后,仍然能够互达。而原来强连通分量外的点和强连通分量内的点之间的边没有变化,以前不能互达现在还是不能,这样保证了仍然是极大的强连通子图。 In the original strongly connected component, every pair of vertices in the subgraph is connected by a path.After reversion, this property doesn't change. And the connectivity of the vertices outside of the subgraph and vertices in the subgraph don't change too. So we can guarantee it still be the maximal strongly connected subgraph.

相关主题
相关文档
最新文档