tarjan求关键连接java_TARJAN 算法求强连通分量

在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。如果有向图G的每两个顶点都强连通,称G是一个强连通图。非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。

下图中,子图{1,2,3,4}为一个强连通分量,因为顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通分量。

1870926.html

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

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

Low(u)=Min{DFN(u),Low(v),(u,v)为树枝边,u为v的父节点{

如果下一个要访问的V节点,没有访问过,则low[U]=min(low[u],low[v])。

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

{如果已经在栈中了,则low[U]=min(low[u],dfn[v])

当DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。

依照上面红色字体的定义,只要当DFN[U]=LOW[U] 便说明此时栈中的元素,从元素U以上的栈中元素都属于同一个强连通分量。

下面给出TARJAN算法的具体代码:

#include

#include

struct

{

int x,y,n;

}e[1000];

int f[1000];

int o;

bool v[1000];

bool vs[1000];

int b[1000][1000],dfn[1000],low[1000],zhan=0,lian,ti=0;

int stick[1000];

int min(int a,int b)

{

return (a

}

void add(int a,int b)

{

o++;

e[o].x=a;

e[o].y=b;

e[o].n=f[a];

f[a]=o;

}

void tanzhan(int u)

{

lian++;

int j=1;

while (stick[zhan]!=u)

{

b[lian][j++]=stick[zhan--];

vs[stick[zhan]]=false;

}

b[lian][j++]=stick[zhan];{B数组记录的是每一个强连通分量,及每一个强连通分量中的元素}

vs[u]=false;

b[lian][0]=j-1;

}

void tarjan(int u)

{

dfn[u]=++ti;

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

stick[++zhan]=u;

vs[u]=true;{VS表示当前元素是否在模拟的栈中}

int t=f[u];

v[u]=true;

while (e[t].y!=0)

{

if (v[e[t].y]==false)

{

tarjan(e[t].y);

low[u]=min(low[u],low[e[t].y]);{由上面红色字体的定义,更新每一个点的LOW值和DFN值}

}else

{

if (vs[e[t].y]==true)

low[u]=min(low[u],dfn[e[t].y]);

}

t=e[t].n;

}//这里没有回溯的过程,因此TARJAN是O(M+N)的算法。

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

}

int main()

{

int m;

freopen("tarjan.in","r",stdin);

freopen("tarjan.out","w",stdout);

scanf("%d",&m);

for (int i=1;i<=m;i++)

{

int a,b;

scanf("%d%d",&a,&b);

add(a,b);

}

for (int i=1;i<=6;i++)

{

if (v[i]==false) tarjan(i);

}

for (int i=1;i<=6;i++)

{printf("%d\n",dfn[i]);

printf("%d\n",low[i]);}

return 0;

}

图中的元素采用模拟链表存储,其中需要注意的是,因为tarjan算法是只会对每一个图中的节点访问一遍,因此不需要回溯(即V数组,只会更新一遍)

{foreverzsz 原创,转载请注明出处.}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值