TARJAN 算法求强连通分量

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

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

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以上的栈中元素都属于同一个强连通分量。

  具体的分步分析过程请参考百度文库(http://wenku.baidu.com/view/ceb92fe2524de518964b7d66.html);

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

#include <cstdio>
#include <cstdlib>
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<b)?a:b;
}
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数组,只会更新一遍)

 

本文地址:http://www.cnblogs.com/foreverzsz/archive/2010/11/07/1870926.html

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

 

转载于:https://www.cnblogs.com/foreverzsz/archive/2010/11/07/1870926.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值