Tarjan's algorithm
Tarjan算法可以用来求有向图的强连通分量个数,之前十分粗略的写了Kosaraju算法,这里打算比较认真的分析一下Tarjan算法,然后给出算法实现代码。
Tarjan算法的主要算法部分也是dfs(深度优先搜索),但利用了重要的额外信息。下面详细分析一下算法执行过程。
再强调一个强连通子图的重要特点:对于强连通子图,有一个特定的事实就是,该子图一定形成环,那么从该子图中任意点出发,总能回到出发点。
基于上面这一点,Tarjan算法通过维护两个存放顶点访问顺序(时间)的数组。如果子图形成环,则将处于环中的每一个顶点的访问顺序置为该环的出发点的访问时间,以表明他们是一个强连通子图。可能你会怀疑进入环后,不会只在环中遍历,可能会跳到其他顶点上。实际上这担心是多余,因为图结构使用邻接链表表示,强连通子图使用dfs进行遍历时,只会寻找与当前顶点连接的出度顶点,而形成环的子图中,会很合理的按顺序遍历完。对于孤立点,则自身就是一个环,即强连通分量。
这就是Tarjan算法的思想,主要的就是维护的两个存储访问顺序的数组,然后,形成环的节点的访问时间都置为该强连通子图的出发点的访问时间。
通过下图可以更直观的理解Tarjan算法的执行过程(图来自维基):
时间复杂度分析:
最坏情况是图G的强连通子图就是其本身(这样的图称为强连通图),这时dfs的消费为 $ O(|V| + |E|) $,最后一次dfs的while循环再消费掉 $ O(V) $,所以dfs()最坏情况为 $ O(|V| + |E|) $。最后tarjan()的总消耗为 $ O (V^2) $。
空间复杂度:
显然 $ O(V) $。
算法实现:
#include "stdafx.h"
#include <iostream>
#include <vector>
#include <stack>
#include <list>
#include <minmax.h>
using namespace std;
const int N = 10010;
list<int> *adj;
stack<int> s;
int vis[N], low[N];
bool onstack[N];
int times = 0, scc = 0;
void addEdge(int u, int v)
{
adj[u].push_back(v);
}
void dfs(int u)
{
vis[u] = low[u] = times++;
s.push(u);
onstack[u] = true;
for (list<int>::iterator i = adj[u].begin(); i != adj[u].end(); ++i)
{
int v = *i;
if (vis[v] == -1)
{
dfs(v);
low[u] = min(low[u], low[v]);
}
else if (onstack[v] == true)
low[u] = min(low[u], vis[v]);
}
if (low[u] == vis[u])
{
while (s.top() != u)
{
int w = s.top();
cout << w << ' ';
onstack[w] = false;
s.pop();
}
int w = s.top();
cout << w << endl;
onstack[w] = false;
s.pop();
scc++;
}
}
void tarjan(int V, int E)
{
adj = new list<int>[V + 1];
list<int> v;
for (int i = 1; i <= V; i++)
{
vis[i] = low[i] = -1;
onstack[i] = false;
}
for (int i = 1; i <= E; i++)
{
int u, w;
cin >> u >> w;
adj[u].push_back(w);
}
for (int i = 1; i <= V; i++)
if (vis[i] == -1)
dfs(i);
cout << "该图的强连通分量个数为:" << scc << endl;
}
int main(int argc, char **argv)
{
int V, E;
cin >> V >> E;
tarjan(V, E);
return 0;
}
代码中dfs()函数的for循环后面的部分用来输出所有强连通子图中的顶点,并求出scc(Strongly Connected Components)个数。
下面以前面wiki图为例测试一下算法。
算法测试结果:
3 2 1
7 6
5 4
8
该图的强连通分量个数为:4
参考:
1.https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
2.https://www.geeksforgeeks.org/tarjan-algorithm-find-strongly-connected-components/
Tarjan's algorithm的更多相关文章
- algorithm@ Strongly Connected Component
Strongly Connected Components A directed graph is strongly connected if there is a path between all ...
- Prim 最小生成树算法
Prim 算法是一种解决最小生成树问题(Minimum Spanning Tree)的算法.和 Kruskal 算法类似,Prim 算法的设计也是基于贪心算法(Greedy algorithm). P ...
- Kruskal 最小生成树算法
对于一个给定的连通的无向图 G = (V, E),希望找到一个无回路的子集 T,T 是 E 的子集,它连接了所有的顶点,且其权值之和为最小. 因为 T 无回路且连接所有的顶点,所以它必然是一棵树,称为 ...
- Kosaraju 算法检测有向图的强连通性
给定一个有向图 G = (V, E) ,对于任意一对顶点 u 和 v,有 u --> v 和 v --> u,亦即,顶点 u 和 v 是互相可达的,则说明该图 G 是强连通的(Strong ...
- Kosaraju 算法查找强连通分支
有向图 G = (V, E) 的一个强连通分支(SCC:Strongly Connected Components)是一个最大的顶点集合 C,C 是 V 的子集,对于 C 中的每一对顶点 u 和 v, ...
- GO语言的开源库
Indexes and search engines These sites provide indexes and search engines for Go packages: godoc.org ...
- 210. Course Schedule II
题目: There are a total of n courses you have to take, labeled from 0 to n - 1. Some courses may have ...
- Go语言(golang)开源项目大全
转http://www.open-open.com/lib/view/open1396063913278.html内容目录Astronomy构建工具缓存云计算命令行选项解析器命令行工具压缩配置文件解析 ...
- [转]Go语言(golang)开源项目大全
内容目录 Astronomy 构建工具 缓存 云计算 命令行选项解析器 命令行工具 压缩 配置文件解析器 控制台用户界面 加密 数据处理 数据结构 数据库和存储 开发工具 分布式/网格计算 文档 编辑 ...
随机推荐
- Quality and CCPC
English foundation: the fractional part 小数部分 disclaimer 免责声明 fictitious 虚构的,编造的;假定的,虚设的;小说式的;假装的 No ...
- [转]Jarvis OJ- [XMAN]level2/3_x64-Writeup
学弟写的挺好的,我就直接转过来了 原文链接:http://www.cnblogs.com/ZHijack/p/7940686.html 两道64位栈溢出,思路和之前的32位溢出基本一致,所以放在一起 ...
- spring项目中 通过自定义applicationContext工具类获取到applicationContext上下文对象
spring项目在服务器启动的时候 spring容器中就已经被创建好了各种对象,在我们需要使用的时候可以进行调用. 工具类代码如下 import org.springframework.beans.B ...
- 使用Unity3d和C#的一些属性来设置特殊行为
使用 Unity 的C#语言 ,利用属性(Attribute)来类定义和变量定义或区分其他的变量,您可以设置一种特殊行为 例如,您添加[SerializeField]属性变量,私有变量标识序列化. [ ...
- mybatis(六):设计模式 - 适配器模式
- 伪数组变数组 js
语法糖:简单的说:用语法糖就是把复杂(有助于让我们理解代码含义)的代码,让我们可以简单的敲出来.这种语法对语言本身功能来说没有什么影响,只是为了方便程序员的开发,提高开发效率 方法一: var a=d ...
- 使用c#做前台页面
1.有很多组件,组件右属性,事件 2.在table中,操作用的是图片 3.打开dialog时,其他窗体不能使用 4.在子窗体编辑完,对后台操作后,在父窗体加载一下数据
- winform学习(1)初识winform
winform是Windows窗体应用程序 在窗体设计界面 单击鼠标右键--查看代码,即可转到Form1.cs的代码界面 从代码界面转到窗体设计界面的三种快捷方法:①双击解决方案资源管理器的 For ...
- js的全局变量与var关键字
var a = '1'; 如果定义在函数外,就是全局变量.如果定义在函数内,就不是全局变量,只能在函数内调用. 但是:如果在函数内定义变量,没有加var关键字,默认表示该变量是全局的.
- bugku getshell
http://123.206.87.240:8002/web9/ 该题是walf严格匹配,通过修改Content-type后字母的大小写可以绕过检测, 然后还有,后缀黑名单检测和类型检测,逐个绕过,如 ...