Tarjan算法分解强连通分量(附详细参考文章)
Tarjan算法分解强连通分量
算法思路:
算法通过dfs遍历整个连通分量,并在遍历过程中给每个点打上两个记号:一个是时间戳,即首次访问到节点i的时刻,另一个是节点u的某一个祖先被访问的最早时刻。
时间戳用DFN数组存储,最早祖先用low数组来存,每次dfs遍历到一个节点u,即让这两个记号等于当前时刻,在后面回溯或者判断的过程中在来更新low,DNF是一定的,因为第一次访问时刻一定。然后遍历u的子节点,也就是跟u相连的点v,依次看子节点的时间戳有没有打上,也就是看他有没有被访问过。\(1\).没有就继续dfs点v,在后来回溯的时候,如果v的low比u的low小,也就是v的某一祖先比u还早被访问,因为u和v是直接相连的,说明u的low也该更小,便更新u的low值。\(2\).如果有,就看这个点有没有在栈里,在的话就看v被访问的时刻是不是比u的low还早,如果是,更新u的low值。
当遍历完当前节点及其子节点后,检查u的low与DFN是否相等,如果相等,说明已经找到了一个强连通区域,就是现在栈中u及后入栈的节点。挨个pop出来即可处理。
当这一个tarjan执行完了后不能立马退出,因为图本身有可能是不连通的。还要在循环里tarjan多次。
以上只是提了下算法的思路,具体的相关概念、讲解、实例和原理都在参考文章里。
下面是两个版本的tarjan代码,一个是基于邻接矩阵,一个是基于链式前向星(具体介绍参见上一篇博客)。
代码:
版本一:邻接矩阵
#include <iostream>
#include <memory.h>
#define max_n 1005
using namespace std;
int DFN[max_n];//记录dfs访问次序
int low[max_n];//记录节点最早可追溯到的祖先
int cnt = 0;//时间戳
int Stack[max_n];//模拟栈
int top = -1;//栈顶
int flag[max_n];//记录节点是否入栈
int number = 0;//强连通分量的个数
int j;//栈弹出节点
int G[max_n][max_n];//图的邻接矩阵
int n;//图的节点数目
void tarjan(int u)
{
DFN[u] = low[u] = cnt++;//访问到这个节点打上时间戳
Stack[++top] = u;//节点入栈
flag[u] = 1;//节点已在栈内
for(int i = 0;i<n;i++)
{
if(G[u][i])//如果u与i相连
{
if(!DFN[i])//如果i节点未被访问过
{
tarjan(u);//继续遍历
low[u] = min(low[u],low[i]);//回溯时更新u点的最早祖先值
}
else if(flag[i]&&low[u]<DFN[i])//如果i被访问过,并且在栈内
{
low[u] = DFN[i];//更新u点的最早祖先值
}
}
if(DFN[u]==low[u])//如果节点之后全访问完了以后,并且DFN值与最早公共祖先值相等,说明已经得到了一个强连通分量
{
number++;//强连通分量个数加一
do
{
j = Stack[top--];//弹出栈中比该点后入栈的所有点
cout << j << " ";
flag[j] = 0;//节点不在栈中
}while(j!=u);//直到把节点u也弹出
cout << endl;
}
}
}
int main()
{
memset(DFN,0,sizeof(DFN));
memset(flag,0,sizeof(flag));
memset(Stack,0,sizeof(Stack));
for(int i = 1;i<=n;i++)//遍历多遍求出所有的强连通分量
{
if(DFN[i])
{
tarjan(i);
}
}
return 0;
}
版本二:链式前向星
#include <iostream>
#include <cstring>
#include <stack>
#include <memory.h>
#define max_n 1005
using namespace std;
int n,m;//n为节点个数,m为边的数目
int idx=0;//时间戳
int Bcnt=0;//强连通分量的个数
int instack[max_n];//记录节点是否在栈内
int dfn[max_n];//dfs访问顺序标号
int low[max_n];//节点的最早公共祖先
int Belong[max_n];//存储节点属于哪一个强连通分量
stack<int> s;//dfs访问时的栈
//链式前向星结构
int head[max_n];
int cnt = 0;
struct
{
int v;
int next;
}e[max_n<<1];
void add(int u,int v)
{
++cnt;
e[cnt].v = v;
e[cnt].next = head[u];
head[u] = cnt;
}
//读入数据
void readdata()
{
int a,b;
memset(head,0,sizeof(head));
cin >> n >> m;
for(int i = 0;i<m;i++)
{
cin >> a >> b;
add(a,b);
}
}
void tarjan(int u)
{
dfn[u] = low[u] = ++idx;
s.push(u);
instack[u] = 1;
int v;
for(int i = head[u];i;i=e[i].next)//遍历u的相连节点
{
v = e[i].v;
if(!dfn[v])//如果未访问过
{
tarjan(v);
low[u] = min(low[u],low[v]);//尝试更新u的low值
}
else if(instack[v])//如果被访问过且在栈中
{
low[u] = min(low[u],dfn[v]);//尝试更新u的low值
}
}
if(low[u]==dfn[u])//找到一个联通分量
{
Bcnt++;
do
{
v = s.top();
s.pop();
instack[v] = 0;
Belong[v] = Bcnt;
}while(u!=v);
}
}
//结果处理
void solve()
{
for(int i = 1;i<=n;i++)
{
if(!dfn[i])
{
tarjan(i);
}
}
for(int i = 1;i<=n;i++)
{
cout << "d " << dfn[i] << " l " << low[i] << endl;
}
cout << Bcnt << "个强连通分量" << endl;
for(int i = 1;i<=Bcnt;i++)
{
cout << "第 " << i << " 个" << endl;
for(int j = 1;j<=n;j++)
{
if(Belong[j]==i)
{
cout << j << " ";
}
}
cout << endl;
}
}
int main()
{
readdata();
solve();
return 0;
}
参考文章:
我大概整理了一下,这些博客会很有帮助
键盘里的青春,全网最!详!细!tarjan算法讲解,https://blog.csdn.net/qq_34374664/article/details/77488976(主要是原理,实例,我之前看了很多博客云里雾里的,这篇看懂了)
玩人,Tarjan算法详解,https://blog.csdn.net/jeryjeryjery/article/details/52829142?locationNum=4(算法流程比较清楚,实现基于邻接矩阵,代码有少许错误)
five20,浅析强连通分量(Tarjan和kosaraju),https://www.cnblogs.com/five20/p/7594239.html(实例再次,代码不错,基于链式前向星)
BYVoid,有向图强连通分量的Tarjan算法,https://www.byvoid.com/zhs/blog/scc-tarjan (我刚开始看的,由于我本身还不太理解,看不太懂,但渐渐明白后它的讲解还是不错的。偶然发现这个是dalao的网站,dalao网站有繁体字版本(好像鸟哥的),大佬貌似还对中国语言有研究,有很多有意思的文章,本来看算法结果看其他的文章去了(逃)
最后推荐一个有趣的网站,画图!https://csacademy.com/app/graph_editor/ 对,就是给节点画图的那种,感觉好方便
觉得不错要不右下角推荐一下?
Tarjan算法分解强连通分量(附详细参考文章)的更多相关文章
- 20行代码实现,使用Tarjan算法求解强连通分量
今天是算法数据结构专题的第36篇文章,我们一起来继续聊聊强连通分量分解的算法. 在上一篇文章当中我们分享了强连通分量分解的一个经典算法Kosaraju算法,它的核心原理是通过将图翻转,以及两次递归来实 ...
- HDU 1269 迷宫城堡 tarjan算法求强连通分量
基础模板题,应用tarjan算法求有向图的强连通分量,tarjan在此处的实现方法为:使用栈储存已经访问过的点,当访问的点离开dfs的时候,判断这个点的low值是否等于它的出生日期dfn值,如果相等, ...
- tarjan算法(强连通分量 + 强连通分量缩点 + 桥(割边) + 割点 + LCA)
这篇文章是从网络上总结各方经验 以及 自己找的一些例题的算法模板,主要是用于自己的日后的模板总结以后防失忆常看看的, 写的也是自己能看懂即可. tarjan算法的功能很强大, 可以用来求解强连通分量, ...
- Tarjan算法【强连通分量】
转自:byvoid:有向图强连通分量的Tarjan算法 Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树.搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断 ...
- Tarjan 算法求 LCA / Tarjan 算法求强连通分量
[时光蒸汽喵带你做专题]最近公共祖先 LCA (Lowest Common Ancestors)_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili tarjan LCA - YouTube Tarj ...
- [学习笔记] Tarjan算法求强连通分量
今天,我们要探讨的就是--Tarjan算法. Tarjan算法的主要作用便是求一张无向图中的强连通分量,并且用它缩点,把原本一个杂乱无章的有向图转化为一张DAG(有向无环图),以便解决之后的问题. 首 ...
- tarjan算法求强连通分量
先上代码: #include <iostream> #include <cstring> #include <vector> #include <stack& ...
- 【算法】Tarjan算法求强连通分量
概念: 在有向图G中,如果两个定点u可以到达v,并且v也可以到达u,那么我们称这两个定点强连通. 如果有向图G的任意两个顶点都是强连通的,那么我们称G是一个强连通图. 一个有向图中的最大强连通子图,称 ...
- tarjan 算法求强连通分量
#include<bits/stdc++.h> #define ll long long using namespace std; const int P=1e6; ; ; const i ...
随机推荐
- VS2015 调试中断点失效或表达式错误的解决办法
题描述:在调试前加了断点,但debug时红色的断点变成透明的圆圈加一个感叹号,执行到该处时也不会停止. 这个问题遇到过几次了,前几次都没怎么注意,有时候是因为复制粘贴了某段代码后就这样了,然后点击撤销 ...
- [精] UBOOT2017+FIT 启动流程详尽分析
开发环境:Nanopi-neo-plus2 软件版本:uboot-2017 软件版本:linux-4.14 买这个板子有一段时间了,并没有全身心的投入在上面,有时间了的话就搞一搞, 这篇随笔算是对这个 ...
- 【记录】【windows】下查看端口是否被占用并杀死该进程
查看端口是否被占用 netstat -aon|findstr "端口号" 比如 netstat -aon|findstr "6340" 杀死该进程 taskki ...
- 前端与算法 leetcode 66. 加一
目录 # 前端与算法 leetcode 66. 加一 题目描述 概要 提示 解析 解法一 解法二 算法 # 前端与算法 leetcode 66. 加一 题目描述 给定一个由整数组成的非空数组所表示的非 ...
- centos7查看防火墙状态、关闭防火墙
查看防火墙状态: firewall-cmd --state 关闭防火墙: systemctl stop firewalld.service 禁止firewall开机启动: systemctl disa ...
- 复杂的sql参考(3)
SELECT apply.assets_code, apply.loan_apply_code, cust.cust_name, cust.id_no, cust.mobile, platform.p ...
- Geoserver发布强制显示标签处理
TextSymbolizer 增加如下配置: <!-- 标签重叠也显示 --> <VendorOption name="conflictResolution" ...
- vscode入门使用教程(页面调试)
初次使用vscode时各种不适应,所有需要用到的功能貌似都需要单独安装插件才能用.这让很多初次使用vscode的朋友有点无所适从. 下面本人就带各位朋友学习下如何使用vscode来进行最基本的工作—— ...
- left join 左边有数据,右边无数据
参考了链接: https://blog.csdn.net/chenjianandiyi/article/details/52402011 主要是and和where的区别: 原Sql: Con ...
- C#设计模式之11:命令模式
C#设计模式之11:命令模式 命令模式 命令模式用来解决一些复杂业务逻辑的时候会很有用,比如,你的一个方法中到处充斥着if else 这种结构的时候,用命令模式来解决这种问题就会让事情变得简单很多. ...