客户端基本不用的算法系列:Tarjan 算法求解强连通份量
在 《Tarjan 算法的思路》中咱们已经给出了 Tarjan 算法中的比较重要的几个元素,咱们在这里从新复习一下:算法
DFN[]
数组:数组存储访问顺序,也就是遍历的点会分配一个序号(从小到大),而后序号存在这个数组当中:DFN[u]
为节点 u 搜索的次序编号;数组LOW[]
数组:表示该点能直接或间接到达时间最小的顶点。例如:LOW[u]
为节点 u 的子树可以追溯到最先的栈中节点的次序号;微信stack
存储该连通子图中的全部点。app
下面咱们来聊一聊这几个东西要怎么用。spa
什么是强连通份量
咱们先给出一个强连通份量的定义:在有向图 G 中,若是两个顶点 u, v 之间存在一条 u 到 v 的路径,也存在一个 v 到 u 的路径,则称这两个顶点 u, v 是强连通的(strongly connected)。若是有向图 G 的任意两个顶点都强连通,则称 G 是一个强连通图。.net
非强连通图有向图的极大强连通子图,称为强连通份量(strongly connected components)。3d
下图中,子图{1,2,3,4}为一个强连通份量,由于顶点1,2,3,4两两可达。{5},{6}也分别是两个强连通份量,总共三个强连通份量。code
算法过程
咱们先给出一个演示 Tarjan 算法的经典图,从根结点 1 开始DFS,把遍历到的节点入栈(1-3-5-6)
,当搜索到 u=6
的时候,DFN[6] = LOW[6]
,当 DFN == LOW
的时候,咱们认为找到一个强连通份量。而后执行弹栈操做,直到 u == v
为止,{6}
为一个强连通份量。component
DFN = 4,LOW = 4
。
以后回溯到 5 节点,发现
DFN[5] == LOW[5]
,同 6 节点同样继续进行弹栈操做,
{5}
为一个强连通份量。
回溯到结点 3,继续 DFS 搜索到结点 4,把 4 进栈。这时发现节点 4 向节点 1 有后向边,节点 1 还在栈中。因此 LOW[4] = 1
。因为节点 6 已经弹栈, 边 (4, 6)
是指向非栈中节点的横叉边,所以不更新 LOW[4]
。回溯返回到结点3,(3, 4)
为树枝边,因此 LOW[3] = LOW[4] = 1
。orm
横叉边:从某个结点指向搜索树中另外一个子树中的某结点的边
树枝边:DFS 时通过的边,即 DFS 搜索树上的边。
回溯到根结点 1,最后 DFS 到点 2。访问边 (2, 4),此时点 4 还在栈中,因此 LOW[2] = DFN[4] = 5
返回 1 后,发现 DFN[1] = LOW[1]
,因此咱们就将栈中的点所有取出,组成一个强连通份量 {1, 3, 4, 2}
。
{1, 3, 4, 2}
,
{5}
,
{6}
。
能够发现,在 Tarjan 算法中,每一个顶点都被访问了一次,且只进出一次栈,每条边也只被访问了一次,因此算法时间复杂度为 O(n + m)
。
这篇文章至此,下一篇咱们开始聊聊如何实现 Tarjan 算法,以及在结题中如何套用 Tarjan 算法模板来求解题目。最后,若是你以为单看文不够生动的话,点击查看原文还会有大佬的视频讲解(原来电子科大的 qscqesze 大神,如今个人同事😁 )
本文分享自微信公众号 - 一瓜技术(tech_gua)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。