C#,图论与图算法,寻找图强连通单元(Strongly Connected Components)的罗伯特·塔扬(Robert Tarjan‘s Algorithm)算法与源程序
Tarjan算法是一种高效的图算法,它利用图的深度优先搜索遍历,在线性时间内找到有向图中的强连通分量。使用的关键思想是,强连通组件的节点在图的DFS生成树中形成子树。
将有向图划分为强连通分量的任务非常重要,在各种问题中有着广泛的应用。用于此目的的两种算法是:
Tarjan算法
Kosaraju算法
本文将探讨Tarjan的算法。
在继续之前,我们将重新讨论图中的两个基本概念/术语:
强连通图
强连通分量
如果有向图的每个顶点都可以从图中的每个其他顶点到达,则称之为强连通图。这意味着每对节点之间将存在一条路径。
对于有向图,强连通分量是一个分区或子图,它不是另一个强连通分量的子图。这意味着强连通分量必须是满足条件的最大子图。此后,我们将使用缩写词SCC来指强连接组件。
算法
它利用了强连通组件的节点在图的DFS生成树中形成子树的特性。
涉及的步骤包括:
dfs在节点上运行,SCC的子树在编码时被删除并记录。
为每个用户维护两个值dfs\u num(u)和dfs\u low(u)。dfs\u num(u)是首次探索节点u时计数器的值。dfs\u low(u)存储可从u访问的最低dfs\u编号,该编号不是另一个SCC的一部分。
在探索节点时,它们会被推到堆栈上。
将探索节点的未探索子节点,并相应地更新dfs\u low(u)。
一个节点遇到dfs\u low(u)==dfs\u num(u)是其强连接组件中的第一个探索节点,堆栈中位于其上方的所有节点都会弹出并分配适当的SCC编号。
算法简述
如果所有顶点对之间都有一条路径,则有向图是强连通的。有向图的强连通分量是最大强连通子图。
我们已经讨论了Kosaraju的强连通组件算法。前面讨论的算法需要对一个图进行两次DFS遍历。本文讨论了Tarjan的算法,该算法只需要一次DFS遍历。
Tarjan算法基于以下事实:
DFS搜索生成DFS树/林
强连接组件构成DFS树的子树。
如果我们可以找到这些子树的头,我们可以打印/存储该子树中的所有节点(包括头),这将是一个SCC。
从一个SCC到另一个SCC没有后边(可以有交叉边,但在处理图形时不会使用交叉边)。
为了找到SCC的头部,我们计算disc和low数组(与铰接点、桥接和双连接组件一样)。如前几篇文章所讨论的,low[u]表示最早访问的顶点(发现时间最短的顶点),该顶点可以从以u为根的子树到达。如果disc[u]=low[u],则节点u为头部。
强连通分量仅与有向图相关,而Disc和Low值与有向图和无向图都相关,因此在上图中,我们采用了无向图。
在上图中,我们显示了一个图和一个DFS树(根据边的遍历顺序,同一个图上可能有不同的DFS树)。
在DFS树中,连续箭头是树边,虚线箭头是后边(DFS树边)
每个节点的Disc和Low值如图所示为(Disc/Low)。
Disc:这是DFS遍历时第一次访问节点的时间。对于节点A、B、C、。。,和J在DFS树中,Disc值为1、2、3、。。,10
低:在DFS树中,树边将我们向前推进,从祖先节点到其子节点之一。例如,从节点C,树边可以将我们带到节点G、节点I等。后边将我们带到后面,从后代节点到其祖先节点之一。
例如,从节点G开始,后边将我们带到E或C。如果我们同时查看树和后边,那么我们可以看到,如果我们从一个节点开始遍历,我们可能会通过树边向下遍历树,然后通过后边向上遍历。例如,从节点E开始,我们可以向下到G,然后再向上到C。类似地,从E开始,我们可以向下到I或J,然后再向上到F。节点的“低”值通过该节点的子树告诉最顶端的可到达祖先(具有最小可能的圆盘值)。因此,对于任何节点,低值都等于其Disc值(节点是其自身的祖先)。然后我们查看它的子树,看看是否有任何节点可以将我们带到它的任何祖先。如果子树中有多条后缘将我们带到不同的祖先,那么我们将使用具有最小圆盘值的后缘(即最上面的后缘)。如果我们看一下节点F,它有两个子树。带有节点G的子树将我们带到E和C。另一个子树只将我们带回到F。这里最上面的祖先是C,其中F可以达到,因此F的低值是3(C的圆盘值)。
基于以上讨论,应该清楚的是,B、C和D的低值为1(因为A是B、C和D可以到达的最顶层节点)。同样,E、F和G的低值为3,H、I和J的低值为6。
对于任何节点u,当DFS启动时,Low将设置为其Disc 1st。
随后将对其每个子级v逐个执行DFS,低值u可在两种情况下更改:
案例1(树边缘):如果尚未访问节点v,则在完成节点v的DFS后,最小值low[u]和low[v]将更新为low[u]。
低[u]=最小值(低[u]、低[v]);
案例2(后缘):当已访问子v时,则最低低[u]和光盘[v]将更新为低[u]。
低[u]=最小值(低[u],圆盘[v]);
在第二种情况下,我们可以用低[v]代替圆盘[v]。答案是否定的。如果你能想一想为什么答案是否定的,那么你可能已经理解了Low和Disc的概念。
边缘类型
相同的Low和Disc值有助于解决其他图形问题,如铰接点、桥接和双连接组件。
为了跟踪在头部扎根的子树,我们可以使用堆栈(访问时继续推节点)。找到头节点后,从堆栈中弹出所有节点,直到将头从堆栈中取出。
为了确保这一点,我们不考虑交叉边,当我们到达一个已经访问过的节点时,我们应该只处理堆栈中存在的已访问节点,否则忽略该节点。
参考:
C#,图(Graph)的数据结构设计与源代码
源代码(POWER BY 315SOFT)
using System;
using System.Text;
using System.Linq;
using System.Collections;
using System.Collections.Generic;namespace Legalsoft.Truffer.Algorithm
{public partial class Graph{private void Strongly_Connect_Components_Utility(int u,ref int[] low,ref int[] disc,ref bool[] stackMember,ref Stack<int> st){disc[u] = time;low[u] = time;time++;stackMember[u] = true;st.Push(u);foreach (int n in Adjacency[u]){if (disc[n] == -1){Strongly_Connect_Components_Utility(n,ref low,ref disc,ref stackMember,ref st);low[u] = Math.Min(low[u], low[n]);}else if (stackMember[n] == true){low[u] = Math.Min(low[u], disc[n]);}}int w = -1;if (low[u] == disc[u]){while (w != u){w = (int)st.Pop();Traversal.Add(w);stackMember[w] = false;}}}public void Strongly_Connect_Components(){int[] disc = new int[Node_Number];int[] low = new int[Node_Number];for (int i = 0; i < Node_Number; i++){disc[i] = -1;low[i] = -1;}bool[] stackMember = new bool[Node_Number];Stack<int> st = new Stack<int>();for (int i = 0; i < Node_Number; i++){if (disc[i] == -1){Strongly_Connect_Components_Utility(i,ref low,ref disc,ref stackMember,ref st);}}}}public static partial class GraphDrives{public static string Strongly_Connect_Components(){StringBuilder sb = new StringBuilder();Graph g1 = new Graph(5);g1.AddEdge(1, 0);g1.AddEdge(0, 2);g1.AddEdge(2, 1);g1.AddEdge(0, 3);g1.AddEdge(3, 4);g1.Strongly_Connect_Components();sb.AppendLine("SSC in first graph<br>");sb.AppendLine(String.Join(",", g1.Traversal.ToArray()) + "<br>");Graph g2 = new Graph(4);g2.AddEdge(0, 1);g2.AddEdge(1, 2);g2.AddEdge(2, 3);g2.Strongly_Connect_Components();sb.AppendLine("SSC in second graph ");sb.AppendLine(String.Join(",", g2.Traversal.ToArray()) + "<br>");Graph g3 = new Graph(7);g3.AddEdge(0, 1);g3.AddEdge(1, 2);g3.AddEdge(2, 0);g3.AddEdge(1, 3);g3.AddEdge(1, 4);g3.AddEdge(1, 6);g3.AddEdge(3, 5);g3.AddEdge(4, 5);g3.Strongly_Connect_Components();sb.AppendLine("SSC in third graph<br>");sb.AppendLine(String.Join(",", g3.Traversal.ToArray()) + "<br>");Graph g4 = new Graph(11);g4.AddEdge(0, 1);g4.AddEdge(0, 3);g4.AddEdge(1, 2);g4.AddEdge(1, 4);g4.AddEdge(2, 0);g4.AddEdge(2, 6);g4.AddEdge(3, 2);g4.AddEdge(4, 5);g4.AddEdge(4, 6);g4.AddEdge(5, 6);g4.AddEdge(5, 7);g4.AddEdge(5, 8);g4.AddEdge(5, 9);g4.AddEdge(6, 4);g4.AddEdge(7, 9);g4.AddEdge(8, 9);g4.AddEdge(9, 8);g4.Strongly_Connect_Components();sb.AppendLine("SSC in fourth graph<br>");sb.AppendLine(String.Join(",", g4.Traversal.ToArray()) + "<br>");Graph g5 = new Graph(5);g5.AddEdge(0, 1);g5.AddEdge(1, 2);g5.AddEdge(2, 3);g5.AddEdge(2, 4);g5.AddEdge(3, 0);g5.AddEdge(4, 2);g5.Strongly_Connect_Components();sb.AppendLine("SSC in fifth graph<br>");sb.AppendLine(String.Join(",", g5.Traversal.ToArray()) + "<br>");return sb.ToString();}}
}
C#,图论与图算法,寻找图强连通单元(Strongly Connected Components)的罗伯特·塔扬(Robert Tarjan‘s Algorithm)算法与源程序相关推荐
- 图论学习六之Strongly connected components强连通分量
强连通分量(Strongly connected cmponents) • 在有向图G中,如果任意两个不同的顶点相互可达,则称该有向 图是强连通的.有向图G的极大强连通子图称为<
- C#,图论与图算法,二分图(Bipartite Graph)的霍普克罗夫特-卡普(Hopcroft Karp)最大匹配算法与源程序
二分图Bipartite graph 有没有可能通过数学过程找到你的灵魂伴侣?大概让我们一起探索吧! 假设有两组人注册了约会服务.在他们注册后,会向他们展示另一组人的图像并给出他们的描述.他们被要求选 ...
- 图论 —— 图的连通性 —— 有桥连通图加边变边双连通图
对于一个有桥的连通图,加边变成边双连通图 1.求出所有的桥,然后删除这些桥边.剩下的每个连通块都是一个双连通子图. 2.把每个双连通子图收缩为一个顶点. 3.加回桥边,统计度为1的节点的个数(叶节点的 ...
- connected components algorithm连通组件算法
connected components algorithm连通图算法 什么是connected components algorithm? 用通俗的话说就是一个图像的前景部分有几部分构成,用下面的这 ...
- C#,图论与图算法,二分图(Bipartite Graph)最佳二分匹配(Maximum Bipartite Matching)算法与源程序
二部图中的匹配是一组边的选择方式,使两条边不共享一个端点.最大匹配是最大大小(最大边数)的匹配.在最大匹配中,如果向其添加任何边,则该边不再是匹配.对于给定的二部图,可以有多个最大匹配. 我们为什么在 ...
- 图论--双连通分量--点双连通模板
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #incl ...
- hdu2767(强连通分量)一个图最少添加几条边能使得该图强连通?
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2767 分析:首先找出强连通分量,然后把每个强连通分量缩成一个点,得到一个DAG.接下来,设有a个结点( ...
- POJ 2375 Cow Ski Area 增加最小边使图强连通
题意:一个矩阵图,每一个格子有一个权值,相邻格子之间可以通,条件是一个格子可以走到相邻格子的条件是相邻格子的全是不大于当前格子,问增加多少条边使得每一个格子都可以到达任意格子 想法:tarjan缩点, ...
- POJ-1236(有向图强连通分量 + 缩点 + 加边使得整个图强连通)
题目:http://poj.org/problem?id=1236 感觉是强连通问题的一道典型题目,还是有很多地方没有注意到,看了discuss才发现: (1)当整个图已经是一个SCC时 (2)对于S ...
最新文章
- soalris小記...
- SqlServer在附加数据库时提示:无法打开物理文件**.mdf 操作系统错误拒绝访问
- JAVA程序通过JNI调用C/C++库
- FreeRTOS任务挂起与解除
- python 测试mysql数据库_Python MySQL 数据库之测试索引
- 无碳小车 matlab,基于无碳小车前轮运动关系的MATLAB运动轨迹仿真.doc
- ModuleNotFoundError: No module named ‘pycocotools‘
- ARM上的Bootloader的具体实现1071098736
- [转载] C++11初始化列表与参数列表的作用
- P4177 [CEOI2008]order
- c++规定浮点数输出格式
- 通过shell和redis来实现集群业务中日志的实时收集分析
- 质性数据分析软件NVivo的安装选项和参数
- python爬取B站弹幕
- 夏天推荐凉快的地方不嫌多!四明山这里更是集凉爽与好玩一起
- chapter3 转录组学
- 手机投屏不是全屏怎么办_手机投屏怎样才能全屏
- 基于Wireshark的TCP SACK重传介绍
- 教你巧用后视镜判断车距
- 芯片短缺并没有妨碍英特尔创下“有史以来业绩最好的一年”