强连通分量

转载地址:http://blog.csdn.net/jokes000/article/details/7538994

算法分类:

图论


问题定义:

有向图强连通分量:

在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。

如果有向图G的每两个顶点都强连通,则称G是一个强连通图。

非强连通图有向图的极大强连通子图,成为强连通分量(strongly connected components)。

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



直接根据定义,用双向遍历取交际的方法求强连通分量,时间复杂度为O(N^2+M)。更好的方法是Kosaraju算法或者Tarjan算法。

两者的时间复杂度都是O(N+M)。本文介绍的是Tarjan算法。


算法原理:(Tarjan)

Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一颗子树。

搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以盘对栈顶到栈中的节点是否为一个强连通分量。


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

由定义可以得出:

Low(u)= Min { DFN(u), Low(v)} ((u,v)为树枝边,u为v的父节点DFN(v),(u,v)为指向栈中节点的后向边(非横叉边))

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


算法时空复杂度:

O(N+M)


代码实现:(hdu1269)

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <vector>  
  4. #include <stack>  
  5. using namespace std;  
  6. #define MIN(a,b) ((a)<(b)?(a):(b))  
  7. #define N 10005             // 题目中可能的最大点数   
  8. stack<int>sta;                // 存储已遍历的结点   
  9. vector<int>gra[N];            // 邻接表表示图   
  10. int dfn[N];                 // 深度优先搜索访问次序   
  11. int low[N];                 // 能追溯到的最早的次序   
  12. int InStack[N];             // 检查是否在栈中(2为在栈中,1为已访问,且不在栈中,0为不在)   
  13. vector<int> Component[N];     // 获得强连通分量结果  
  14. int InComponent[N];         // 记录每个点在第几号强连通分量里  
  15. int index,ComponentNumber;  // 索引号,强连通分量个数   
  16. int n, m;                   // 点数,边数   
  17.   
  18. void init(void)  
  19. {  
  20.     memset(dfn, 0, sizeof(dfn));  
  21.     memset(low, 0, sizeof(low));  
  22.     memset(InStack, 0, sizeof(InStack));  
  23.     index = ComponentNumber = 0;  
  24.     for (int i = 1; i <= n; ++ i)  
  25.     {  
  26.         gra[i].clear();  
  27.         Component[i].clear();  
  28.     }  
  29.       
  30.     while(!sta.empty())  
  31.         sta.pop();  
  32. }  
  33.   
  34. void tarjan(int u)  
  35. {  
  36.     Instack[u] = 2;  
  37.     low[u] = dfn[u] = ++ index;  
  38.     sta.push(u);  
  39.   
  40.     for (int i = 0; i < gra[u].size(); ++ i)  
  41.     {  
  42.         int t = gra[u][i];  
  43.         if (dfn[t] == 0)  
  44.         {  
  45.             tarjan(t);  
  46.             low[u] = MIN(low[u], low[t]);  
  47.         }   
  48.         else if (InStack[t] == 2)  
  49.         {  
  50.             low[u] = MIN(low[u], dfn[t]);  
  51.         }  
  52.     }  
  53.   
  54.     if (low[u] == dfn[u])  
  55.     {  
  56.         ++ ComponentNumber;  
  57.         while (!sta.empty())  
  58.         {  
  59.             int j = sta.top();  
  60.             sta.pop();  
  61.             InStack[j] = 1;  
  62.             Component[ComponentNumber].push_back(j);  
  63.             InComponent[j]=ComponentNumber;  
  64.             if (j == u)  
  65.                 binputak;  
  66.         }  
  67.     }  
  68. }  
  69.    
  70. void input(void)  
  71. {  
  72.     for(int i=1;i<=m;i++)  
  73.     {  
  74.         int a,b;  
  75.         scanf("%d%d",&a,&b);  
  76.         gra[a].push_back(b);  
  77.     }  
  78. }  
  79.    
  80. void solve(void)  
  81. {  
  82.     for(int i=1;i<=n;i++)  
  83.         if(!dfn[i])  
  84.             tarjan(i);  
  85.     if(ComponentNumber>1)  
  86.         puts("No");  
  87.     else  
  88.         puts("Yes");  
  89. }  
  90.    
  91. int main()  
  92. {  
  93.     while(scanf("%d%d",&n,&m),n+m)  
  94.     {  
  95.         init();  
  96.         input();  
  97.         solve();  
  98.     }  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值