前面为转载的。后面是自己的理解。

三种tarjan算法(上)

。这篇算是做一个总结吧。
  1. 求强连通分量
  2. 求无向图的割和桥
  3. 最近公共祖先

求强连通分量

基本概念:

      强连通是有向图才有的概念。一个有向图是强连通的是对于每个有序对 u,v,存在一条从 u到v  的路径。一个有向图的强连通分量是指它的极大连通子图。就是你可以从 a地走到 b 地,同样可以从b地走到a地,a,b就是连通的。
      下图(图片来自这里)中 顶点 {1,2,3,4}是一个强连通分量,因为它们两两都可以到达。直观上来讲,所谓的强连通,(如果有至少有一个顶点)就是可以至少形成一个环。如1->3->4->1就是一个环,它们就是强连通的。注意也有像{5},{6}这样的的连通分量。
      强连通的tarjan 算法是基于图的深度优先搜索(这个经常用于获取图的各种信息)。下面说一下几个约定: 
  1. 时间戳       :DFN[i]是指结点i被遍历的时间。
  2. Low[i]       :是指在搜索树中,结点i和其子孙可以访问到的最早的祖先,Low[i] = Min(DFN[i], DFN[j], Low[k])其中j是i的祖先(我们把子孙连到祖先的边叫后向边),k是i 的子女。
  3. 结点的颜色:color[i]是用于标示结点i的状态:白色指还没到搜索到,灰色正在被搜索,黑色处理完毕。在实际操作中用-1,0,1分别代表白色、灰色、黑色。

tarjan算法的步骤:

  1. 先把所有的结点的颜色都初始化白色,并把栈清空。
  2. 找到一个白色的结点i(即结点的颜色为白色)
  3. 给结点一个时间戳,把结点圧入栈中,并把结点标记为灰色。令Low[i] = DFN[i]
  4. 遍历结点i 的每条边(i,j)。若color[j]是白色,就对结点i重复2~5步骤。并令Low[i] = min(Low[j],low[i]).如果color[j]是灰色,令Low[i] = min(Low[i],DFN[j])。如果是黑色不做任何处理。
  5. 把结点的颜色改为黑色,如果Low[i] = DFN[i],就把从栈顶到结点i间的元素弹出
  6. 重复步骤2,至到没有白色顶点

下面是算法的一个模板:

[cpp] view plaincopy
  1. <pre name="code" class="cpp">#include <math.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <stdlib.h>
  5. //从顶点0开始
  6. // 要用的话要初始化:调用Adj.initial 和 tarjan.initial
  7. //要解决问题用调用tarjan.solve
  8. //对tarjan.initial要传入的参数是图边集Adj,和顶点个数n
  9. const int maxn = 11000;
  10. //顶点的规模
  11. const int maxm = 210000;
  12. //边的规模,如果是无向图要记得乘以2
  13. const int GRAY = 0;
  14. const int WHITE =-1;
  15. const int BLACK = 1;
  16. typedef struct Edge{
  17. int s;
  18. int e;
  19. int next;
  20. }Edge;
  21. typedef struct Adj{
  22. int edge_sum;
  23. int head[maxn];
  24. Edge edge[maxm];
  25. void initial(){
  26. edge_sum = 0;
  27. memset(head,-1,sizeof(head));
  28. }
  29. void add_edge(int a, int b){
  30. edge[edge_sum].s = a;
  31. edge[edge_sum].e = b;
  32. edge[edge_sum].next = head[a];
  33. head[a] = edge_sum++;
  34. }
  35. }Adj;
  36. typedef struct Tanjan{
  37. int n;
  38. int *head;
  39. Adj *adj;
  40. Edge *edge;
  41. int cnt;
  42. int top;
  43. int cur;
  44. int dfn[maxn];
  45. int low[maxn];
  46. int color[maxn];
  47. int stack[maxn];
  48. int belong[maxn];
  49. void initial(Adj *_adj,int _n){
  50. n = _n;
  51. adj = _adj;
  52. head = (*adj).head;
  53. edge = (*adj).edge;
  54. }
  55. void solve(){
  56. memset(dfn,-1,sizeof(dfn));
  57. memset(color,WHITE,sizeof(color));
  58. top = cnt = cur = 0;
  59. for(int i = 0; i < n; i++)
  60. if(color[i] == WHITE)//找到一个白色的顶点,就开始处理
  61. tarjan(i);
  62. }
  63. inline int min(int a, int b){
  64. if(a < b) return a;
  65. else return b;
  66. }
  67. void tarjan(int i){
  68. int j = head[i];
  69. color[i] = GRAY;//标记为灰色
  70. stack[top++] = i;//把结点圧入栈顶
  71. dfn[i] = low[i] = ++cur;//给结点一个时间戳,并给Low初始化
  72. while(j != -1){
  73. int u = edge[j].e;
  74. if        (dfn[u] == WHITE){
  75. tarjan(u);
  76. low[i] = min(low[i],low[u]);
  77. //更新low
  78. }else  if (color[u] == GRAY)
  79. low[i] = min(low[i],dfn[u]);
  80. //一条后向边
  81. j = edge[j].next;
  82. }
  83. color[i] = BLACK;
  84. if(low[i] == dfn[i]){
  85. do{
  86. j = stack[--top];
  87. belong[j] = cnt;
  88. }while(i != j);
  89. ++cnt;
  90. }
  91. }
  92. }Tarjan;
  93. Adj adj;
  94. Tarjan tj;</pre><br>
  95. <br>
  96. <pre></pre>
  97. <pre></pre>
  98. <pre></pre>
  99. <pre></pre>
  100. <pre></pre>

tarjan算法的简单证明:

         首先,这边再重复一下什么是后向边:就是在深度优先搜索中,子孙指向祖先的边。在一棵深度优先搜索树中,对于结点v, 和其父亲结点u而言,u,v 属于同一个强连通分支的充分必要条件是  以v为根的子树中,有一条后向边指向u或者u的祖先。
1 、必要性。  
           如果 u,v属于同一个强连通分支则必定存在一条 u到 v的路径和一条v到u的路径。合并两条则有 u->v->v1->v2->..vn->u, 若顶点v1到vn都是v 的子孙,则有 vn->u这样一条后向边。
          如果v1到vn 不全是vn的子孙,则必定有一个是u的祖先,我们不妨设vi为u的祖先,则有一条后向边 V[i-1] ->v[i]。
2.、充分性。    我们设 u1->u2->u3..->un->u->v->v1->v2..->vn,我们假设后向边vn指向ui则有这样一个环:u[i]->u[i+1]...->u->v->v1->v2..->v[n-1]->v[n]->u[i],易知,有一条u->v的路径,同时有v->u的路径。固u,v属于同一连通分支。
        在算法开始的时候,我们把i圧入栈中。根据low[i] 和 dfn[i]的定义我们知道,
        如果low[i] < dfn[i] 则以i为顶点的子树中,有指向祖先的后向边,则说明i和i的父亲为在同一连通分支,也就是说留在栈中的元素都是和父结点在同一连通分支的。
         如果low[i] == dfn[i],则 i为顶点的子树中没有后向边,那么由于  留在栈中的元素都是和父结点在同一连通分支的,我们可以知道,从栈顶到元素i构成了一个连通分支。显然,low[i]不可能小于dfn[i]。
__________________________________________________________________________________________________----我的理解,感觉有点类似于并查集,一开始假设有n个连通分量既dfn,low是我们进行判断后修正的既low[n]=m表示实际上n这点属于m这个连通分量,个点属于哪个连通分量。然后进行深搜当找到一个环既(Instack[j]==true)这次找到的点是我们之前搜索到的,那这个点就属于dfn[j]这个连通分量。
#define M 5010//题目中可能的最大点数
int STACK[M],top=0;//Tarjan算法中的栈
bool InStack[M];//检查是否在栈中
int DFN[M];//深度优先搜索访问次序int Low[M];//能追溯到的最早的次序
int ComponentNumber=0;//有向图强连通分量个数
int Index=0;//索引号
vector<int> Edge[M];//邻接表表示
vector<int> Component[M];//获得强连通分量结果
int InComponent[M];//记录每个点在第几号强连通分量里
int ComponentDegree[M];//记录每个强连通分量的度void Tarjan(int i)
{int j;DFN[i]=Low[i]=Index++;InStack[i]=true;STACK[++top]=i;for (int e=0;e<Edge[i].size();e++){j=Edge[i][e];if (DFN[j]==-1){Tarjan(j);Low[i]=min(Low[i],Low[j]);}elseif (InStack[j]) Low[i]=min(Low[i],DFN[j]);}if (DFN[i]==Low[i]){ComponentNumber++;do{j=STACK[top--];InStack[j]=false;Component[ComponentNumber].push_back(j);InComponent[j]=ComponentNumber;}while (j!=i);}
}

tarjan算法不是很懂先mark一下。相关推荐

  1. 【转】BYV--有向图强连通分量的Tarjan算法

    转自beyond the void 的博客: https://www.byvoid.com/zhs/blog/scc-tarjan 注:红色为标注部分 [有向图强连通分量] 在有向图G中,如果两个顶点 ...

  2. 有向图强连通分量tarjan算法

    转自:http://www.byvoid.com/blog/scc-tarjan/ http://blog.csdn.net/geniusluzh/article/details/6601514 在有 ...

  3. byvoid 神牛的tarjan算法讲解!

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

  4. 有向图强连通分量的Tarjan算法——转自BYVoid

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

  5. [转载] 有向图强连通分量的Tarjan算法 ——byvoid

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

  6. 有向图强连通分量的Tarjan算法

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

  7. 有向图强连通分量之Tarjan算法

    出处https://www.byvoid.com/zhs/blog/scc-tarjan [有向图强连通分量] 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly con ...

  8. 强连通分量及缩点tarjan算法解析

    http://blog.csdn.net/justlovetao/article/details/6673602 有向图强连通分量的Tarjan算法 [有向图强连通分量] 在有向图G中,如果两个顶点间 ...

  9. 寻找强连通分量的Tarjan算法

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

最新文章

  1. MPLS是如何工作?MPLS的完整指南和常见问题解答
  2. 硬件产品研发,除了电子元器件成本,还有什么成本?
  3. [ZJOI2014] 璀璨光华(bfs建图 + dfs搜索)
  4. 【渝粤题库】国家开放大学2021春2626药事管理与法规题目
  5. 光遇安卓服务器维修,《光遇》渠道服更换手机解决办法
  6. 计算机组装与维护推荐教材,计算机组装与维护(全国高等专科教育计算机类规划教材)...
  7. 服务器上如何修改伪静态,服务器如何设置伪静态和301重定向
  8. 操作系统—进程的状态与状态的转换
  9. 简单记录 Part1.1
  10. [需求管理-9]:需求规格说明书SRS
  11. Unity 导航网格的使用
  12. nmap 扫描服务器开放了哪些端口
  13. EEGLAB 脑电数据处理与分析
  14. 凌恩生物明星产品:一文读懂细胞器基因组!
  15. SQL中数据类型转换
  16. 解决redis Could not get a resource since the pool is exhausted 问题
  17. Vue入门笔记Day 8
  18. java 变量 英文_Java中的字符类型变量无论是中文,英文还是数字,都是占4字节。...
  19. linux双系统如何选择顺序,Ubuntu和Windows双系统选择开机顺序
  20. java 房贷计算器代码_用JAVA编程一个房贷计算器

热门文章

  1. 2022-2028年中国自主可控行业深度调研及投资前景预测报告(全卷)
  2. Gin 框架学习笔记(03)— 输出响应与渲染
  3. debian10 apache2使用ssl
  4. 微服务架构必备的几点知识
  5. 垃圾回收 内存管理 python
  6. 如何将模糊的扫描版pdf转为清晰的pdf或word_pdf问题小结
  7. OpenCL框架与示例
  8. 自然语言推理和数据集
  9. 服务器技术综述(一)
  10. 斯坦福大学李飞飞团队图像分类课程笔记