📅  最后修改于: 2023-12-03 14:50:32.624000             🧑  作者: Mango
双连通分量是一种无向图的子图,其中任意两个顶点都至少有两条不同的路径相连,且子图中任意一条边都属于至少两个简单环。
给定无向图 $G=(V,E)$ ,一个双连通分量是 $G$ 的连通子图,且任意两点间至少有两条不同的简单路径,任意一条边在该连通子图中至少属于两个简单环。
计算一个无向图中的所有双连通分量,常用的算法有以下几种:
其中 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 算法的核心,对于其他算法的实现也非常有帮助。
双连通分量的应用也很广泛,如在计算网络的稳定性、计算最小割、判断图的可分区域等领域中都有应用。因此,学好双连通分量的相关算法,对于程序员来说也是非常重要的。