求强连通分量的Kosaraju算法和Tarjan算法的比较 by ljq
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
求强连通分量的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)
mark1[x]=true;
node *i;
for(i=edge1[x];i!=NULL;i=i->next) {
if(!mark1[i->vex])
{
DFS1(i->vex);
}
}
T[Tcnt]=x;
Tcnt++;
}
void DFS2(int x)
{
mark2[x]=true;
node *i;
for(i=edge2[x];i!=NULL;i=i->next) {
if(!mark2[i->vex])
{
DFS2(i->vex);
}
}
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m))
{
if(n==0&&m==0)
{
break;
}
int i,a,b;
for(i=1;i<=n;i++)
{
mark1[i]=mark2[i]=false;
edge1[i]=NULL;
edge2[i]=NULL;
}
node *t;
while(m--)
{
scanf("%d%d",&a,&b);
t=(node *)malloc(sizeof(node));
t->vex=b;
t->next=edge1[a];
edge1[a]=t;
t=(node *)malloc(sizeof(node));
t->vex=a;
t->next=edge2[b];
edge2[b]=t;
}
Tcnt=0;
for(i=1;i<=n;i++)
{
if(!mark1[i])
{
DFS1(i);
}
}
Bcnt=0;//Bcnt用于记录强连通分量的个数for(i=Tcnt-1;i>=0;i--)
{
if(!mark2[T[i]])
{
DFS2(T[i]);
Bcnt++;
}
}
if(Bcnt==1)//如果强连通分量的个数为1则说明该图是强连通图
{
printf("Yes\n");
}
else
{
printf("No\n");
}
}
return 0;
}
四、Tarjan算法
时间复杂度:O(M+N)注:M代表边数,N代表顶点数。
所需的数据结构:原图、标记数组(与Kosaraju算法的标记不同,这里的标记数组是标记顶点是否在栈内)、一个栈、dfn数组(记录顶点被遍历的时间)、low数组(记录顶点或顶点的子树能够追溯到的最早的栈中顶点的遍历时间)。
算法描叙:
步骤1: 找一个没有被遍历过的顶点v,进行步骤2(v)(遍历时间由1开始累加,若是非连通图,则须重复进行步骤1)。否则,算法结束。
步骤2(v): 初始化dfn[v]和low[v]的值为当前的遍历时间,并且v进栈;
对于v所有的邻接顶点u:
(1)如果u没有被遍历过,则进行步骤2(u),同时维护low[v]。
(2)如果u已经被遍历过,但还在栈中(即还不属于任一强连通分量),则维护low[v],否则不做任何操作。
如果有dfn[v]==low[v],则把栈中的顶点弹出(直到把v都弹出为止),这些顶点组成一个强连通分量。
hdu1269(Tarjan算法)代码:
#include
#include
#include