【笔记/模板】Tarjan 算法与有向图的强连通分量
有向图的强连通分量
定义
对于一张有向图 ,对于两点 ,都存在一条路径可以使得 达到 , 也可以达到 ,则称该两个点是互相可达的。
如果一张有向图上的任意两个节点之间可以互相可达,则称这张图是 强连通的(Strongly Connected)。
对于一张子图 ,并且不存在其他的连通图 使得 ,则称子图 是有向图 的一个 强连通分量(Strongly Connected Component,SCC),叫做极大的连通分量。
应用
对于一个较为复杂的图论问题中,利用强连通分量的缩点技巧,将每一个强连通分量中的所有点看作一个点,最终使得图转化为 有向无环图(DAG),大大的减少了时间复杂度和解题难度。
Tarjan 算法
DFS 生成树
对于一张图的深度优先遍历,根据节点的先后遍历顺序,我们可以生成出一棵树,这棵树即为 DFS生成树,它的性质有如下几点:
- 深度优先遍历的第一个点是该树的根节点。
- 遍历一个节点的所有边时,如果找到一个没有访问过的节点,则这条边被称为树边(tree edge),如果找到了一个访问过的节点,则可能是返祖边(back edge)或者横叉边(cross edge)。除此之外,还有一种边叫做前向边(forward edge),它是在搜索的时候遇到子树中的节点的时候形成的。
- 如果结点 是某个强连通分量在搜索树中遇到的第一个结点,那么这个强连通分量的其余结点肯定是在搜索树中以 为根的子树中。结点 被称为这个强连通分量的根。
时间戳
在 Tarjan 算法中我们引入时间戳的概念,表示着DFS生成树上遍历节点的顺序。
我们维护一下几个变量:
- :表示深度优先搜索遍历节点时节点 的时间戳。
- :表示已 为根的子树中时间戳最小的一个节点的时间戳。
因为一个节点的子树内的 都大于该节点的 ,不难证明:
从根开始的一条路径上的 严格递增, 严格非降。
Tarjan 算法
因此我们很容易想到,在一个强连通分量中,有且只有一个节点 使得 ,这个节点也是该分量中的根节点。因此,在深度优先搜索的回溯过程中,如果 ,则该节点 与搜索的节点构成一个 SCC。
问题就在于如何快速的求出被节点 所更新的其他节点,也就是由 构成的 SCC 的剩余节点。
由于 dfs 的本质是一个栈,我们可以维护一个栈用来记录更新的节点信息,直到回溯到 时,从栈顶到 的所有节点便是这个 SCC 的所有节点。
对于当前节点 和遍历节点 ,模拟方式如下:
- 如果 未被访问:对 进行深度有些搜索,同时将回溯后的 来更新 。
- 如果 被访问过,且没有出栈:说明 当且不属于任何一个 SCC,直接用 更新 。
- 如果 被访问且不在栈中:说明 所在的 SCC 已经被处理,忽略。
模板代码
初始化
1 | int n, m; // 存图用的边数 |
核心代码
1 | void tarjan(int ver) |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Thy's Blog!