当前位置: 首页 > news >正文

【图论】Tarjan算法(强连通分量)

一、Tarjan算法简介

Tarjan算法是一种由美国计算机科学家罗伯特·塔杨(Robert Tarjan)提出的求解有向图强连通分量的线性时间的算法。

二、强连通分量的概念

在有向图 G G G 中,如果任意两个不同的顶点相互可达,则称该有向图是强连通的。
如图1所示,图“郓城武安张”就是一个强连通图。
图1
图1

如果在强连通图 G G G 中进行加边操作得到有向图 G ′ G' G,那么我们称原图 G G G G ′ G' G 的一个强连通分量。
如图2所示,图“郓城武安张”、图“同”和图“学”都是图“郓城武安张同学”的一个强连通分量。
图2
图2

三、初识Tarjan算法

Tarjan算法本质上是基于深度优先搜索的一种算法。
在使用Tarjan算法时,需要用到两个辅助数组:
d n f x dnf_x dnfx:记录每个点被发现时的时刻。
l o w x low_x lowx:表示以顶点x为根的子树中所有顶连出的边能到达的最早的能到达x的顶。
通俗一点,对于图2中的图,为了应对汉字无法作为下标的问题,我们简化一下,得到下面的图3。
图3
图3

假定 1 1 1 号结点为搜索起点,那么我们将有:

  • d n f i = { 1 , 2 , 3 , 4 , 5 , 6 , 7 } dnf_i=\{1,2,3,4,5,6,7\} dnfi={1,2,3,4,5,6,7}
  • l o w i = { 1 , 1 , 1 , 1 , 1 , 6 , 7 } low_i=\{1,1,1,1,1,6,7\} lowi={1,1,1,1,1,6,7}

如果对上面的描述有疑惑,请往下看。

四、模拟Tarjan算法

我们继续看上面的图3,并对它进行模拟(假定 1 1 1 号结点为搜索起点),深入地了解Tarjan算法。
为了帮助理解,求图3中的有向图以 1 1 1 号结点为根的最小生成树,如图4。
图4
图4

会发现,这棵树是一条链的结构。

  1. 初始化: d n f i = 0 dnf_i=0 dnfi=0,对于每一步, l o w i = d n f i low_i=dnf_i lowi=dnfi
  2. 1 1 1 号结点进入程序,并将其入栈,将 d n f 1 dnf_1 dnf1 的值赋为 1 1 1
    栈: 1 1 1
  3. 1 1 1 号结点可以直接到达 2 2 2 号结点,且 d n f 2 = 0 dnf_2=0 dnf2=0,将 2 2 2 号结点入栈,并将将 d n f 2 dnf_2 dnf2 的值赋为 2 2 2
    栈: 1 2 1~2 1 2
  4. 2 2 2 号结点可以直接到达 3 3 3 号结点,且 d n f 3 = 0 dnf_3=0 dnf3=0,将 3 3 3 号结点入栈,并将将 d n f 3 dnf_3 dnf3 的值赋为 3 3 3
    栈: 1 2 3 1~2~3 1 2 3
  5. 3 3 3 号结点可以直接到达 4 4 4 号结点,且 d n f 4 = 0 dnf_4=0 dnf4=0,将 4 4 4 号结点入栈,并将将 d n f 4 dnf_4 dnf4 的值赋为 4 4 4
    栈: 1 2 3 4 1~2~3~4 1 2 3 4
  6. 4 4 4 号结点可以直接到达 5 5 5 号结点,且 d n f 5 = 0 dnf_5=0 dnf5=0,将 5 5 5 号结点入栈,并将将 d n f 5 dnf_5 dnf5 的值赋为 5 5 5;(注意啦,下一步是重点!)
    栈: 1 2 3 4 5 1~2~3~4~5 1 2 3 4 5
  7. 5 5 5 号结点可以直接到达 1 1 1 号和 6 6 6 号两个结点,按字典序先遍历 1 1 1 号结点,因为 d n f 1 = 1 ≠ 0 dnf_1=1\neq0 dnf1=1=0,此时我们需要进行出栈操作;
    栈: 1 2 3 4 5 1~2~3~4~5 1 2 3 4 5
  8. 将栈顶 l o w 5 low_5 low5 的值赋值为 min ⁡ ( l o w 5 , d n f 1 ) = 1 \min(low_5,dnf_1)=1 min(low5,dnf1)=1,并将 5 5 5 出栈,将栈顶 l o w 4 low_4 low4 的值赋值为 min ⁡ ( l o w 4 , d n f 5 ) = 1 \min(low_4,dnf_5)=1 min(low4,dnf5)=1,并将 4 4 4 出栈;
  9. 依此类推,直至 1 1 1 被出栈;
    栈:空
  10. 5 5 5 号结点继续往下搜索, 5 5 5 号结点可以直接到达 6 6 6 号结点,且 d n f 6 = 0 dnf_6=0 dnf6=0,将 6 6 6 号结点入栈,并将将 d n f 6 dnf_6 dnf6 的值赋为 6 6 6
    栈: 6 6 6
  11. 6 6 6 号结点可以直接到达 7 7 7 号结点,且 d n f 7 = 0 dnf_7=0 dnf7=0,将 7 7 7 号结点入栈,并将将 d n f 7 dnf_7 dnf7 的值赋为 7 7 7
    栈: 6 7 6~7 6 7
  12. 7 7 7 号结点没有可以直接到达的结点,回溯(回溯过程省略);
    栈: 6 7 6~7 6 7
  13. 此时,整个图都已经被搜索完毕,但是栈中仍存在原神元素,说明栈中剩余的每个结点都自成一个强连通分量,我们只需依次出栈即可。
    栈:空
  14. 最终得到第三部分所提及的两个数组。

代码:

int step;
stack<int>st;
int dnf[300006],low[300006];
bool in_st[300006];
void tarjan(int x){int tmp;step++;dnf[x]=low[x]=step;st.push(u);in_st[x]=1;for(int i=head[x];i!=-1;i=nxt[i]){tmp=e[i];if(!dnf[tmp]){tarjan(tmp);low[x]=min(low[x],low[tmp]);}else if(in_st[tmp])low[x]=min(low[x],dnf[tmp]);}if(low[x]==dnf[x]){do{tmp=st.top();st.pop();in_st[tmp]=0;}while(tmp!=x);}return;
}

如果博客有错误,请联系我,我会尽快修正!压力马斯内!


http://www.mrgr.cn/news/9700.html

相关文章:

  • 文件包含漏洞案例
  • 【C++】01背包问题暴力,记忆,动态规划解法
  • 分布式锁 redis与zookeeper
  • 幂等性简介
  • 基于x86 平台移植ffmpeg3.4.5及ffmpeg验证
  • 汽车租赁|基于SprinBoot+vue的汽车租赁系统(源码+数据库+文档)
  • SpringBoot的内置缓存以及整合第三方缓存
  • 【25届秋招】Shopee 0825算法岗笔试
  • Long Short-Term Memory
  • JavaScript性能对决:左移运算符VS乘法运算,谁更胜一筹?
  • 自动驾驶-机器人-slam-定位面经和面试知识系列10之高频面试题(04)
  • Facebook的AI助手:如何提升用户社交体验的智能化
  • Linux系统下的容器安全:深入解析与最佳实践
  • ThinkPHP6异步请求的全面解析
  • Linux文件IO缓存
  • Web API 学习笔记 第四弹
  • JavaScript学习文档(5):为什么需要函数、函数使用、函数传参、函数返回值、作用域、匿名函数、逻辑中断
  • SQLite使用datetime函数
  • 集合及数据结构第七节————LinkedList的模拟实现与使用
  • Redis下载安装使用教程图文教程(超详细)