Tarjan算法【强连通分量】

安稳与你 提交于 2020-02-12 12:29:16

转自:byvoid:有向图强连通分量的Tarjan算法

Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。搜索时,把当前搜索树中未处理的节点加入一个堆栈回溯时可以判断栈顶到栈中的所有节点是否为一个强连通分量。

有两个概念:1.时间戳,2.追溯值

时间戳是dfs遍历节点的次序。

定义DFN(u)为节点u搜索的次序编号(时间戳),Low(u)为u或u的子树能够追溯到的栈中节点最小的次序号。由定义可以得出:

1 Low(u)=min{
2     DFN(u),   // 自己的次序号
3     Low(v),   //(u,v)为树枝边,u为v的父节点
4     DFN(v),   //(u,v)为指向栈中节点的后向边(非横叉边)
5 }

即以下节点的最小值:

1. 自己、子树节点的次序号

2. 指向栈中节点(后向边节点)的次序号[等价于 DFN(v)<DFN(u)且v不为u的父亲节点],这里不是横叉边(指向不在栈中的节点)。

DFN(u)=Low(u)时,以u为根的搜索子树上所有节点是一个强连通分量。

伪码:

 1 tarjan(u)
 2 {
 3     DFN[u]=Low[u]=++Index     // 为节点u设定次序编号和Low初值
 4     Stack.push(u)             // 将节点u压入栈中
 5     for each (u, v) in E      // 枚举每一条邻边
 6         if (v is not visted)  // 如果节点v未被访问过
 7             tarjan(v)         // 继续向下找
 8             Low[u] = min(Low[u], Low[v])     
 9         else if (v in S)      // 如果节点v还在栈内
10             Low[u] = min(Low[u], DFN[v])
11     if (DFN[u] == Low[u])     // 如果节点u是强连通分量的根
12         repeat
13             v = S.pop         // 将v退栈,为该强连通分量中一个顶点
14             print v
15         until (u== v)
16 }

运行Tarjan算法的过程中,每个顶点都被访问了一次,且只进出了一次堆栈,每条边也只被访问了一次,所以该算法的时间复杂度为O(N+M)

一个顶点u是割点,当且仅当满足(1)或(2)

(1) u为树根,且u有多于一个子树。

(2) u不为树根,且满足存在(u,v)为树枝边(或称父子边,即u为v在搜索树中的父亲),使得DFN(u)<=Low(v)。即:若某点的子树们能回到的点大于等于自己,则该点为割点

一条无向边(u,v)是,当且仅当(u,v)为树枝边,且满足DFN(u)<Low(v)。

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!