Tarjan 算法
Taijan算法是求有向图强连通分量的算法。
Tarjan 算法主要是在 DFS 的过程中维护了一些信息:dfn、low 和一个栈。
- 栈里存放当前已经访问过但是没有被归类到任一强连通分量的结点。
- dfn[u] 表示 DFS 第一次搜索到 u 的次序。
- Low(u) 记录该点所在的强连通子图所在子树的根节点的 Dfn 值。
基本思路:
在深度搜索中会搜索到已访问的节点,产生环,即连通分量的一部分, 环与环的并集仍是连通分量。
由于深度搜索总是会回溯,所以将强连通图中最早搜索到的节点认为是根节点,它的dfn 和 low都是最小的。
之后会深度搜索子树的所有节点, 确定所有与回边相关的环,在出现环时, 环上的节点的值就会统一为环上最小的值。
直到回溯到根节点 dfn = low 的标志, 输出连通分量, 其他节点low相同, 但dfn要比根节点大。
至于( u, v )访问到新的节点,在初始化下 v 的 low 值是递增的 , 但是 v 在进入深度搜索后,它返回的值会变化。
当 v 出现在环中被处理了, v 的 low 值会变小。此时 u 也一定在环中 , v 的low 值变小, 是由于有一条回边连接到的 u 的父辈的节点
, 父辈的节点 - 顶点 - u - v - 父辈的节点 构成了一个环。
伪码解析:
Tarjan(u)
{
DFN[u]=Low[u]=++Index // 为节点u递增地设定次序编号和Low初值
Stack.push(u) // 存放已经访问过但没有被归类到任一强连通分量的结点
for each (u, v) in E //深度搜索
if ( v is not visted ) {
Tarjan( v )
Low [ u ] = min( Low[ u ] , Low[ v ] ); // 初始化下 low[ v ] 比较大, 若 low [ v ]变小 , v 在环中,u 也在环中, 且两个环相连通。
}
else if (v in S) // 如果节点v还在栈内,出现回边,出现环
Low [ u ] = min( Low[ u ], DFN[ v ] )
if (DFN[u] == Low[u]) // 如果节点u是强连通分量的根
v = S.pop // 将v退栈,
print v
until (u== v)
}