tarjan有向图的强连通

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了tarjan有向图的强连通相关的知识,希望对你有一定的参考价值。

强连通:在有向图G中,两个顶点间至少存在一条路径,则两个点强连通。

强连通图:在有向图中,每两个顶点都强连通,则有向图G就是一个强连通图。

强连通分量:在非强连通图中的极大强连通子图,就称为强连通分量。

直接根据定义,可以通过双向遍历取交集的方法求强连通分量,但是其复杂度为O(N^2+M)。更好的方法是用tarjan算法,其时间复杂度为O(N+M)。

 

tarjan:其实就是对图的深度优先遍历。

算法模拟:

定义 DFN [u]为节点u被搜索到时的次序编号(也就是所遍历的第几个);

定义LOW[U]为U或者U 的子数能够追溯到最早的栈中节点的次序号。

  (当DFN[U] ==  LOE[U] 时,以U为根的搜索子树上所有节点是一个强连通分量。实质上就是这个点的整个子树回溯完了,没有链接的路了)

 

技术分享

此时的F点DNF[F]==LOW[F]=4,所以F为一个强连通分量,只有它自己本身。

然后返回节点E,此时发现 DFN[E] == LOW[E] =5,所以E也单独为一个强连通分量,只有他本身。

然后返回节点C:

技术分享

从C节点继续搜其他的C的子树,搜到节点D,将D加入栈中。然后D有  D ->  F ,但是F 已经出栈,所以F节点直接跳过,

还有一个节点A , 但是呢,A节点已经在栈中,所以LOW[D] = DFN[A] = 1,然后是一个回溯的过程,所以呢,LOW[C] = LOW [D] =1;

所以说明这三个节点已经是一个强连通分量了。

继续回到节点A,最后访问B节点,,然后 B-> D ,然而 D点还在栈中,所以LOW[B] = DFN [ D ] = 5 ,DFN[B] = 6 ;

没有其他点了,算法结束,最后得到 {A,B,C,D} ,{E}, {F}为强连通分量。

在tarjan算法中,每个点只被访问了一次,而且只进行了一次的栈,每条边也只被访问了一次,。所以该算法的时间复杂度为O(N+M);

程序模板:

 

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <iostream>
  4 using namespace std;
  5 
  6 #define N 100
  7 #define M 100
  8 
  9 struct node
 10 {
 11     int v;
 12     int next;
 13 };
 14 
 15 node edge[M];//边的集合
 16 
 17 int a[N];//顶点的集合
 18 int instack[N];//模拟栈,标记是否在栈中
 19 int stack[N];
 20 int belong[N];//各个顶点属于哪个强连通分量
 21 int DFN[N];
 22 int LOW[N];//栈中能够追溯到最早的节点序号
 23 int n;//点的个数
 24 int m;//边的个数
 25 int count;//边的计数器
 26 int index;//序号(时间戳
 27 int top;
 28 int sun;//强连通分量的个数
 29 
 30 //用邻接表存储 
 31 void add_edge(int u,int v)
 32 {
 33     edge[count].next=a[u];
 34     edge[count].v=v;
 35     a[u]=count++;
 36 } 
 37 
 38 void tarjan(int u)
 39 {
 40     int i,j;
 41     int v;
 42     DFN[u]=LOW[u]=++index;
 43     instack[u]=true;
 44     stack[++top]=u;//进栈 
 45     for(i=a[u];i!=-1;i=edge[i].next)
 46     {
 47         v=edge[i].v;
 48         if(!DFN[v])//如果DFN[v]为0,没被访问 
 49         {
 50             tarjan(v);
 51             //一个回溯的过程 
 52             if(LOW[v]<LOW[u])
 53                 LOW[u]=LOW[v];
 54         }
 55         else
 56         {
 57             if(instack[v]&&DFN[v]<LOW[u])
 58                 LOW[u]=DFN[v];
 59         }
 60     }
 61     if(DFN[u]==LOW[u])
 62     {
 63         sun++;
 64         //出栈 
 65         do
 66         {
 67             j=stack[top--];
 68             instack[j]=false;
 69             belong[j]=sun;
 70         }while(j!=u);
 71         
 72     }
 73 }
 74 
 75 void solve()
 76 {
 77     int i;
 78     top=index=sun=0;
 79     memset(DFN,0,sizeof(DFN));
 80     memset(LOW,0,sizeof(LOW));
 81     for(i=1;i<=n;i++)
 82     {
 83         if(!DFN[i])
 84             tarjan(i);
 85     }
 86 }
 87 
 88 int main()
 89 {
 90     int i,j,k;
 91     count=0;
 92     memset(a,-1,sizeof(a));
 93     scanf("%d %d",&n,&m);
 94     for(i=1;i<=m;i++)
 95     {
 96         scanf("%d %d",&j,&k);
 97         add_edge(j,k);
 98     }
 99     solve();
100     for(i=1;i<=n;i++)
101         printf("%d ",belong[i]);
102 }

 

以上是关于tarjan有向图的强连通的主要内容,如果未能解决你的问题,请参考以下文章

『Tarjan算法 有向图的强连通分量』

寻找图的强连通分量:tarjan算法简单理解

Tarjan

强连通分量tarjan模板复习

强连通分量tarjan缩点——POJ2186 Popular Cows

强连通分量的Tarjan算法