Tarjan 系列算法学习总结 - 有向图强连通分量,无向图割顶和桥,无向图双连通分量

 

Tarjan算法求有向图强连通分量


 

常用场景:

  • 求顶点基:求出强连通分量后缩点,得到DAG。在入度为0的点中,每个强连通分量中任取一个点,可构成顶点基。

核心思想:

  • 注意每个节点在一个且仅在一个强连通分量中。
  • 当dfs第一次访问到某一个强连通分量时,将这个被初次访问的节点记为根节点,其所在的强连通分量必定被包含在以此节点为根的DFS子树之中。
  • 一个点为根节点,当且仅当其本身和其子节点无法访问到比该节点更早的节点。
  • $dfn[i]$保存节点dfs序,$low[i]$保存节点所能访问到的dfs序最靠前的点,根据定义来更新。
     1 // 求强连通分量,通常可以对有向图中强连通分量进行缩点。生成DAG图
     2 #include <stack>
     3 #include <vector>
     4 using namespace std;
     5 
     6 const int maxn = 100000;
     7 
     8 //接口: 邻接表 多组数据时 需要所有过程量初始化为0;
     9 vector <int> G[maxn];
    10 
    11 //说明: Tar 为记录未处理的回溯栈
    12 //dfn[i] 第i个节点的dfs序
    13 //low[i] 第i个节点所能访问到的最早祖先(dfs序值)
    14 //注意 由于随意取点 因此dfs要将所有的点遍历 节点编号是从 1开始
    15 stack <int> Tar;
    16 int dfn[maxn], low[maxn];
    17 bool instack[maxn];
    18 int index = 0;
    19 int Bcnt = 0;
    20 //结果 Bcnt 表示强连通分量的数目
    21 int Tarjan(int i)
    22 {
    23         dfn[i] = low[i] = ++index;
    24         instack[i] = true;
    25         Tar.push(i);
    26         for (int j = 0; j < G[i].size(); j++)
    27         {
    28                 if (!dfn[G[i][j]])
    29                 {
    30                         Tarjan(G[i][j]);
    31                         if (low[i] > low[G[i][j]]) low[i] = low[G[i][j]];
    32                 }
    33                 else if (instack[G[i][j]] && low[i] > dfn[G[i][j]]) low[i] = dfn[G[i][j]];
    34         }
    35         if (dfn[i] == low[i]) {
    36                 Bcnt++;
    37 
    38                 while(!Tar.empty())
    39                 {
    40                         int j = Tar.top();
    41                         Tar.pop();
    42                         instack[j] = false;
    43                         /* j是当前第Bcnt个强连通分量中的节点
    44                                 按需要操作
    45                         */
    46                         if (j == i) break;
    47 
    48                 }
    49         }
    50 }
    View Code

     

Tarjan算法求无向图割点和桥


与求强连通分量的思想类似:

  • 每次开始dfs最先访问的点即dfs树的树根,该点是割点,当且仅当存在两个或者更多子节点时,其是割点。
  • 非根节点$u$是割点,当且仅当其存在一个子节点$v$,使得$v$及其子节点都无法通过除了边$(u,v)$ 外访问回其根节点的祖先,即 $ dfn[u] \leq low[v] $。
  • $(u,v)$ 是桥当且仅当$v$及其子节点都无法通过除了边$(u,v)$ 外访问回其根节点及其祖先,即 $ dfn[u] < low[v] $。注意割点的定义,桥并不要求dfs树树根特判。
  • 实现细节上,由于无向图边的双向性,dfs时需要同时考虑其父亲节点不能重复访问。
  • 代码板子待补

Tarjan算法求无向图双连通分量


无向图中双连通分量分为点双连通和边双连通。

边双连通图:将所有的桥删去,剩下各联通块的部分就是双连通分量;

点双连通图:

  • 有几个需要注意的点,首先栈内保存的应当是边,而不是点,因为双连通分量之间可能共享某一个割点。
  • 从刘汝佳训练指南的板子中我们需要一点更正,这个模板求出的双连通分量的定义应该是“子图内部不含割点的连通分量”,当该连通分量所含点数大于2时,才存在环。

 

posted @ 2016-11-07 16:43  Connor.Zhong  阅读(1125)  评论(0)    收藏  举报