ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【图论】—— 有向图的强连通分量 [打印本页]

作者: 吴旭华    时间: 2022-6-25 21:29
标题: 【图论】—— 有向图的强连通分量


给定有向图 ,若存在 ,满足从 出发能到达 中所有的点,则称 是一个“流图”Flow Graph ),记为  ,其中, 称为流图的源点
在一个流图  上从  进行深度优先遍历,每个点只访问一次。所有发生递归的边  (换言之,从  到  是对  的第一次访问)构成一棵以  为根的树,我们把它称为流图  的搜索树
同时,在深度优先遍历的过程中,按照每一个节点第一次被访问的时间顺序,依次给予流图中 N 个节点 1~N 的整数标记,称为时间戳,记为 
   流图中的每条有向边  必然是以下四种之一:
   如下图“流图”以及其搜索树所示: 
加粗的表示的是树枝边,并构成一棵搜索树。 


有向图的强连通分量 

    给定一张有向图。若对于图中的任意两个结点 ,既存在从  到  的路径,也存在从  到  的路径,则称该有向图是“强连通图”
 
  有向图的极大连通子图称为“强连通分量”,简记为 SCC(Strongly Connected Component)。
  此处的“极大”的含义和双连通分量的“极大”的含义类似。
  Tarjan算法基于有向图的深度优先遍历,能够在线性的时间里求出一张有向图的强连通分量。
一个“环”一定是强连通图。如果既存在从  到 的路径, 也存在从  到  的路径,那么  显然在一个环中。因此,Tarjan算法的基本思路就是对每个点,尽量找到与它一起能够构成环的所有节点。
容易发现,“前向边”  没有什么用处,因为搜索树上本来就存在 从  到 的路径。
“后向边”  非常有用,因为它可以从搜索树上 从  到  的路径一起构成环。
“横向边”  视情况而定,如果从  出发能够找到一条回到  的祖宗节点,那么  就是有用的。

为了找到通过“后向边”和“横叉边”构成的换,Tarjan算法在深度优先遍历的同时维护一个栈。
当访问到结点 x 时,栈中需要保存以下两类节点:
综上所述,栈中的节点就是能与从 x 出发的“后向边”和“横叉边”形成环的节点。进而可以引入“追溯值”的概念。

追溯值

 设  表示流图的搜索树中以 x 为根的子树。x 的追溯值  定义为满足以下条件的节点的最小时间戳:
    根据定义,Tarjan算法按照以下步骤计算“追溯值”:
     下页图中的中括号【】里的数值标注了每个节点的的“追溯值”


强连通分量判定法则 

 在追溯值的计算过程中,若从 x 回溯前,有  成立,则栈中从 x 到 栈顶的所有节点构成一个强连通分量
大致来说,在计算追溯值的第三步,如果  ,那么说明  中的节点不能与栈中其他结点一起构成环。另外,因为横叉边的终点时间必然小于起点时间戳,所以中的结点也不可能直接到达尚未访问的结点(时间戳更大)。综上所述,栈中从 x 到栈顶的所有节点不能与其他结点构成环。
由因为我们及时进行了判定和出栈操作,所以从 x 到栈顶的所有节点独立构成一个强连通分量
 Tarjan算法模板

  1. void tarjan(int u)
  2. {
  3.     dfn[u] = low[u] = timestamp;
  4.     stk[++ top] = u, in_stk[u] = true;
  5.    
  6.     for(int i = h[u]; ~i; i = ne[i])
  7.     {
  8.         int j = e[i];
  9.         if(!dfn[j])
  10.         {
  11.             tarjan(j);
  12.             low[u] = min(low[u], low[j]);
  13.         }
  14.         else if(in_stk[j])
  15.             low[u] = min(low[u], dfn[j])
  16.     }
  17.    
  18.     if(dfn[u] == low[u])
  19.     {
  20.         int y;
  21.         ++ scc_cnt;
  22.         do{
  23.           y = stk[top ++ ];
  24.           in_stk[y] = false;
  25.           id[y] = scc_cnt;
  26.         }while(y != u)
  27.     }
  28. }
复制代码

缩点 

我们可以把每一个 SCC 缩成一个点。对于原图中的每条有向边  若  ,则在编号为  与编号为  的SCC之间连边。
最终,我们会得到一个有向无环图(DAG)
[code]for(int x = 1; x




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4