连通性问题 之 Tarjan算法求强连通分量

如果有向图G的任何两定点都可以相互到达,则称图G是强连通图,如果有向图G存在两顶点u 和v ,使得u不能到达v或者v不能到达u则称G是非强连通图。

如果有向图G不是强连通图,它的子图G‘是强连通图,点v属于G' 任意包含v的强连通子图也是G’ 的子图,则称G‘ 是有向图G的巨大强连通子图,也称强连通分量。

Tarjan  算法思路:

  如果对原图进行深度优先搜索,由于强连通分量的定义可知,任何一个强连通分量是原图的深度优先搜索的子树。那么,只要确定强连通分量子树的根,然后根据这些根从树的最底层开始,一个一个地取出强连通分量即可。那么剩下的问题就是如何确定强连通分量的根和如何从底层开始拿出强连通分量了。

对于确定的强连通分量的根,在这里维护两个数组,一个是dfn[  ],另一个是low [  ],其中dfn[ v]表示顶点v被访问的时间,low[ v] 为与顶点v,相连接的未删除的顶点u的low[u ]和low[v]  最小值(low[v ]初始化为dfn[ v])。这样,在一次深度优先遍历的回溯过程中,如果发现low[ v]==dfn[v],那么当前顶点点就是一个强连通分量的根。因为如果不是强连通分量的根,那么一定属于另一个强连通分量,而且它的根是当前顶点的祖宗,那么存在包含当前顶点的到其祖宗的回路,可知low[ v]一定被更改为一个比dfn[ v],更小的值。

对于如何取出强连通分量,这个比较简单,如果当前节点为一个强连通分量的根,那么它的强连通分量一定是一个是以该根为根节点的子树。在深度优先遍历的时候维护一个堆栈,每次访问一个新节点,就压入堆栈。由于当前节点是这个强连通分量中最先被压入堆栈的,那么在当前节点以后压入堆栈的并且仍在堆栈中的节点都属于这个强连通分量。假设一个节点在当前节点压入堆栈以后压入并且还存在,同时不属于该强连通分量,那么一定属于另一个强连通分量,但当前的节点是其根的祖宗,那么这个强连通分量应该在此之前已经被取出。

伪代码:

tarjan(u)
{
  DFN[u]=Low[u]=++Index       // 为节点u设定次序编号和Low初值
  Stack.push(u)                   // 将节点u压入 栈中
  for each (u, v) in E              // 枚举每一条边
    if (!dfn[v])          // 如果节点v未被访问过
{
      tarjan(v)               // 继续向下找
      Low[u] = min(Low[u], Low[v])
}
    else if (v in S)             // 如果节点v还在栈内
      Low[u] = min(Low[u], DFN[v])
  if (DFN[u] == Low[u])        // 如果节点u是强连通分量的根
do{
      v = S.pop            // 将v退栈,为该强连通分量中一个顶点
    }while(u != v);
}
看看人家是怎么写的吧~~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值