求强连通分量tarjan算法.docx

上传人:b****6 文档编号:8156667 上传时间:2023-01-29 格式:DOCX 页数:8 大小:185.83KB
下载 相关 举报
求强连通分量tarjan算法.docx_第1页
第1页 / 共8页
求强连通分量tarjan算法.docx_第2页
第2页 / 共8页
求强连通分量tarjan算法.docx_第3页
第3页 / 共8页
求强连通分量tarjan算法.docx_第4页
第4页 / 共8页
求强连通分量tarjan算法.docx_第5页
第5页 / 共8页
点击查看更多>>
下载资源
资源描述

求强连通分量tarjan算法.docx

《求强连通分量tarjan算法.docx》由会员分享,可在线阅读,更多相关《求强连通分量tarjan算法.docx(8页珍藏版)》请在冰豆网上搜索。

求强连通分量tarjan算法.docx

求强连通分量tarjan算法

Forpersonaluseonlyinstudyandresearch;notforcommercialuse

求强连通分量的tarjan算法

强连通分量:

是有向图中的概念,在一个图的子图中,任意两个点相互可达,也就是存在互通的路径,那么这个子图就是强连通分量。

(如果一个有向图的任意两个点相互可达,那么这个图就称为强连通图)。

如果u是某个强连通分量的根,那么:

(1)u不存在路径可以返回到它的祖先。

(2)u的子树也不存在路径可以返回到u的祖先。

∙例如:

∙强连通分量。

在一个非强连通图中极大的强连通子图就是该图的强连通分量。

比如图中子图{1,2,3,5}是一个强连通分量,子图{4}是一个强连通分量。

 

tarjan算法的基础是深度优先搜索,用两个数组low和dfn,和一个栈。

low数组是一个标记数组,记录该点所在的强连通子图所在搜索子树的根节点的dfn值,dfn数组记录搜索到该点的时间,也就是第几个搜索这个点的。

根据以下几条规则,经过搜索遍历该图和对栈的操作,我们就可以得到该有向图的强连通分量。

算法规则:

∙数组的初始化:

当首次搜索到点p时,Dfn与Low数组的值都为到该点的时间。

∙堆栈:

每搜索到一个点,将它压入栈顶。

∙当点p有与点p’相连时,如果此时(时间为dfn[p]时)p’不在栈中,p的low值为两点的low值中较小的一个。

∙当点p有与点p’相连时,如果此时(时间为dfn[p]时)p’在栈中,p的low值为p的low值和p’的dfn值中较小的一个。

∙每当搜索到一个点经过以上操作后(也就是子树已经全部遍历)的low值等于dfn值,则将它以及在它之上的元素弹出栈。

这些出栈的元素组成一个强连通分量。

∙继续搜索(或许会更换搜索的起点,因为整个有向图可能分为两个不连通的部分),直到所有点被遍历。

算法伪代码:

tarjan(u){  DFN[u]=Low[u]=++Index//为节点u设定次序编号和Low初值  Stack.push(u)//将节点u压入栈中  foreach(u,v)inE//枚举每一条边    if(!

dfn[v])//如果节点v未被访问过

{      tarjan(v)//继续向下找      Low[u]=min(Low[u],Low[v])

}    elseif(vinS)//如果节点v还在栈内      Low[u]=min(Low[u],DFN[v])  if(DFN[u]==Low[u])//如果节点u是强连通分量的根

do{      v=S.pop//将v退栈,为该强连通分量中一个顶点    }while(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)。

 

此外,该Tarjan算法与求无向图的双连通分量(割点、桥)的Tarjan算法也有着很深的联系。

学习该Tarjan算法,也有助于深入理解求双连通分量的Tarjan算法,两者可以类比、组合理解。

 

应用例子:

(OJ1484popularcows)

题意:

有N只牛,输入a,b的话,则说明b关注a,而且关注有传递性。

例如c关注b,且b关注a,则c也关注a。

题目问有多少只奶牛能被其他所有的奶牛关注。

把题目的模型转换:

N个顶点的有向图,有M条边。

求一共有多少个点,满足这样的条件:

所有其它的点都可以到达这个点。

这个点满足条件的充要条件是:

这个点是树中唯一的出度为0的点。

先求强连通分量,然后可以把强连通分量缩成一个点,因为,在强连通分量中的任意两个点可以到达,所有的点具有相同的性质,即它们分别能到达的点集都是相同的,能够到达它们的点集也是相同的。

然后就重新构图,缩点后的图是没有强连通分量的,否则,可将环上所有点也缩成一个点,与极大强联通分量矛盾。

所以只要找出度为0的点,并且出度为0的点只有1个,如果出度为0的点有多个的话,问题则无解。

代码:

#include

#include

#defineadj10010

#defineedg50010

structnode

{

intv;

intnext;

};

nodeedge[edg];

nodeedge1[edg];

intlow[adj],dfn[adj],Stack[adj];

intfirst[adj],first1[adj],fuck[adj];

boolins[adj];

intn,m,temp,cnt,top,count;

intcnt_size[adj],belong[adj],outdegree[adj];

voidcreat(intu,intv)

{

edge1[count].next=first1[u];

edge1[count].v=v;

first1[u]=count++;

}

voidtarjan(intu)

{

inti,v;

dfn[u]=low[u]=++temp;

Stack[top++]=u;

ins[u]=true;

for(i=first[u];i!

=-1;i=edge[i].next)

{

v=edge[i].v;

if(!

dfn[v])

{

tarjan(v);

if(low[u]>low[v])

low[u]=low[v];

}

elseif(ins[v])

{

if(low[u]>dfn[v])

low[u]=dfn[v];

}

}

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

{

intj;

do

{

top--;

j=Stack[top];

ins[j]=false;

cnt_size[cnt]++;

belong[j]=cnt;

}while(u!

=j);

cnt++;

}

}

intmain()

{

inti,j,k,v,sum,num;

inte=0;

scanf("%d%d",&n,&m);

memset(first,-1,sizeof(first));

for(k=0;k

{

scanf("%d%d",&i,&j);

edge[e].v=j;

edge[e].next=first[i];

first[i]=e;

e++;

}

memset(dfn,0,sizeof(dfn));

memset(ins,false,sizeof(ins));

temp=cnt=top=0;

memset(cnt_size,0,sizeof(cnt_size));

memset(low,0,sizeof(low));

for(i=1;i<=n;i++)求强连通分量

{

if(!

dfn[i])

tarjan(i);

}

memset(first1,-1,sizeof(first1));

count=0;

for(i=1;i<=n;i++)重新构造图

{

for(j=first[i];j!

=-1;j=edge[j].next)

{

v=edge[j].v;

if(belong[i]!

=belong[v])

creat(belong[i],belong[v]);

}

}

memset(outdegree,0,sizeof(outdegree));

for(i=0;i

{

for(j=first1[i];j!

=-1;j=edge1[j].next)

outdegree[i]++;

}

sum=num=0;

for(i=0;i

{

if(outdegree[i]==0)求节点为0的个数

{

sum=cnt_size[i];

num++;

}

}

if(num==1)

printf("%d\n",sum);

else

printf("0\n");

return0;

}

结束!

以下无正文

仅供个人用于学习、研究;不得用于商业用途。

 толькодлялюдей,которыеиспользуютсядляобучения,исследованийинедолжныиспользоватьсявкоммерческихцелях. 

Forpersonaluseonlyinstudyandresearch;notforcommercialuse.

NurfürdenpersönlichenfürStudien,Forschung,zukommerziellenZweckenverwendetwerden.

Pourl'étudeetlarechercheuniquementàdesfinspersonnelles;pasàdesfinscommerciales.

Forpersonaluseonlyinstudyandresearch;notforcommercialuse

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 工学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1