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 构建工具 缓存 云计算 命令行选项解析器 命令行工具 压缩 配置文件解析器 控制台用户界面 加密 数据处理 数据结构 数据库和存储 开发工具 分布式/网格计算 文档 编辑 ...
随机推荐
- 简述python(threading)多线程
一.概述 import threading 调用 t1 = threading.Thread(target=function , args=(,)) Thread类的实例方法 # join():在子线 ...
- MonkeyRunner Mac环境 录制脚本和回放 批量回放
1.MonkeyRunner是AndroidSDK自带的一个东西,在SDK目录中的tools\bin文件夹中 2.配置环境变量 编辑环境变量:打开终端输入:open ~/.bash_profile 将 ...
- C#处理不同的JSON数据
https://blog.csdn.net/dayu9216/article/details/78465681 网络中数据传输经常是xml或者json,现在做的一个项目之前调其他系统接口都是返回的xm ...
- spring_boot 加入 mybatis
第一步: <!-- mybatis启动器 自动包含jdbc所以不需要再次引入jdbc依赖 --> <dependency> <groupId>org.mybatis ...
- L1-2 倒数第N个字符串
思路 这题就是一道进制转换,用26进制表示一个数,以及26进制下的数的加减操作. 代码 #include <bits/stdc++.h> using namespace std; int ...
- [thinkphp] 启用__PUBLIC__
我真是受够了,,, 为了解决__PUBLIC__不能用的问题 我折腾了好几天了,然后终于被我找到了原因 解决过程 首先必须贴出来帮助我的人 https://my.oschina.net/u/12630 ...
- D. Game with modulo 交互题(取余(膜)性质)附带a mod b<a/2证明
D. Game with modulo 交互题(取余(膜)性质) 题意 猜一个点\(a\)可以向机器提问 点对\((x,y)\) 如果\(x\mod(a)>=y\mod(a)\)回答\(x\) ...
- python调用sqlite
参考资料:https://www.liaoxuefeng.com/wiki/1016959663602400/1017801751919456 https://www.cnblogs.com/lia ...
- SQL基础语法—insert语句
1 insert语句 insert语句用于插入数据到表中,其基本语法有以下三种: Syntax: INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IG ...
- DataGridView编辑后立即更新到数据库的两种方法
DataGridView控件是微软预先写好的一个显示数据的控件,功能非常强大,可以显示来自数据库表的数据和XML等其他来源的数据. 方法一:基于DataAdapter对象创建一个CommandBuli ...