zoukankan      html  css  js  c++  java
  • 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 }
  • 相关阅读:
    IDE-Android Studio 导入Ecplise项目不改变结构
    IDE-Android Studio -FAQ-使用习惯(不断更新 欢迎留言)
    IDE-Ecplise-代码注释 模版 编码规范 配色
    android- 远程调试
    phpstorm所有快捷键表格pdf
    phpstorm修改字体和大小
    phpstorm重构代码形式让阅读更简单
    七牛云到底好不好使用经历分享
    一篇文章搞懂php中类型转换
    彻底解决php判断a==0为真引发的问题-类型转换
  • 原文地址:https://www.cnblogs.com/ouyang_wsgwz/p/7189702.html
Copyright © 2011-2022 走看看