📌  相关文章
📜  双连通分量(1)

📅  最后修改于: 2023-12-03 14:50:32.624000             🧑  作者: Mango

双连通分量

双连通分量是一种无向图的子图,其中任意两个顶点都至少有两条不同的路径相连,且子图中任意一条边都属于至少两个简单环。

定义

给定无向图 $G=(V,E)$ ,一个双连通分量是 $G$ 的连通子图,且任意两点间至少有两条不同的简单路径,任意一条边在该连通子图中至少属于两个简单环。

计算方法

计算一个无向图中的所有双连通分量,常用的算法有以下几种:

  • Tarjan 算法
  • Hopcroft-Tarjan 算法
  • 以深度遍历为基础的算法

其中 Tarjan 算法是最常用的一种算法,这里我们就介绍一下 Tarjan 算法的实现。

Tarjan 算法

Tarjan 算法就是一个基于深度优先搜索的算法,通过各个顶点进行搜索,遍历整个图,同时维护一个栈,用于记录访问过的顶点。在遍历顶点的过程中,会为每个顶点设置一个代表值 dfn(v) 和一个 low(v) 值,其中 dfn(v) 表示顶点 v 在搜索中被访问的次序,low(v) 表示顶点 v 在一个子树内访问到的最早祖先顶点的次序。

Tarjan 算法的关键是利用 dfn(v)low(v) 这两个值辨别顶点 v 是否为关节点(即使该顶点被删去后图 G 不连通)。如果一个顶点不为关节点,则其可以在一起形成一个双连通分量。

伪代码如下:

idx = 0  # 全局计数器
stack = []  # 栈,用于记录访问过的顶点
ans = []    # 存储所有双连通分量

def tarjan(u, v):
    global idx
    dfn[u] = low[u] = idx
    idx += 1
    stack.append(u)

    for i in G[u]:
        if not dfn[i]:
            tarjan(i, u)
            low[u] = min(low[u], low[i])
            if low[i] >= dfn[u]:
                if len(stack) > 1 and (i != v or G[i].count(u) > 1):
                    ans.append(stack[-3:] + [u])
                else:
                    ans.append(stack[-2:] + [u])
            stack.pop()
        elif i != v:
            low[u] = min(low[u], dfn[i])

其中,两个顶点 $u$ 和 $v$ 在同一个双连通分量中的条件是:$dfn(u) \leq low(v)$ 且 $dfn(v) \leq low(u)$ 。如果满足这个条件,就可以用类似于并查集的方式将 $u$ 和 $v$ 合并。

总结

双连通分量的计算过程中,每个节点的 dfs 序号(dfn)和这个点所能够最早及到的祖先节点的 dfs 序号(low)非常重要,这也是 Tarjan 算法的核心,对于其他算法的实现也非常有帮助。

双连通分量的应用也很广泛,如在计算网络的稳定性、计算最小割、判断图的可分区域等领域中都有应用。因此,学好双连通分量的相关算法,对于程序员来说也是非常重要的。