xxx 寻找割点(Articulation Points)问题
字数 2085 2025-10-28 00:29:09
xxx 寻找割点(Articulation Points)问题
题目描述:
在一个无向连通图中,如果一个顶点被移除后(同时移除与该顶点相连的所有边),图会变得不连通,那么这个顶点就被称为“割点”或“关节顶点”。寻找割点问题要求找出给定无向连通图中的所有割点。例如,在网络设计中,割点可能代表单点故障,识别它们对提高网络鲁棒性很重要。
解题过程:
我们将使用基于深度优先搜索(DFS)的Tarjan算法来解决这个问题。该算法通过一次DFS遍历就能找出所有割点。
核心概念:
- DFS生成树: 从某个根节点开始进行DFS遍历,遍历过程中经过的边构成一棵树,称为DFS生成树。
- 发现时间(discovery time): 每个节点在DFS中被首次访问的时间戳,记为
disc[u]。 - 最低访问时间(low value): 对于节点u,
low[u]表示从u出发,通过树边以及一条反向边(即连接到已访问过的祖先节点的边)所能到达的节点的最小发现时间。
算法步骤:
-
初始化:
- 维护一个全局变量
time,用于在DFS中分配时间戳。 - 为每个节点维护两个数组:
disc[]: 记录节点的发现时间(初始化为-1,表示未访问)。low[]: 记录节点的low值(初始化为-1)。
- 维护一个
parent[]数组,记录DFS树中每个节点的父节点(根节点的父节点为-1)。 - 准备一个布尔数组
ap[](Articulation Point),初始化为false,用于标记节点是否为割点。
- 维护一个全局变量
-
DFS遍历:
- 从任意一个未访问的节点开始进行DFS。
- 当访问节点u时:
a. 设置disc[u] = low[u] = ++time。
b. 初始化一个变量children,用于记录在DFS树中u的直接子节点数量(仅对根节点判断割点时有用)。
c. 遍历u的所有邻居v:
- 如果v未被访问:
- 设置parent[v] = u。
-children加1。
- 递归地对v进行DFS。
- 递归返回后,更新u的low值:low[u] = min(low[u], low[v])。
- 检查割点条件:
- 情况1:u是根节点。如果u是根节点,并且它有至少两个子节点(children >= 2),那么u是割点。因为移除u后,它的各个子树将变得不连通。
- 情况2:u不是根节点。如果存在u的一个子节点v,使得low[v] >= disc[u],那么u是割点。这意味着v及其后代无法通过一条反向边“绕开”u而连接到u的祖先。因此,移除u后,v所在的子树将与图的其他部分断开。
- 如果v已被访问,并且v不是u的父节点(即遇到一条反向边),则更新u的low值:low[u] = min(low[u], disc[v])。这里使用disc[v]而不是low[v],因为我们是通过一条边直接连接到v。
-
输出结果:
- 遍历所有节点,所有
ap[u]为true的节点u即为图的割点。
- 遍历所有节点,所有
举例说明:
考虑一个无向图:顶点集{0,1,2,3},边集{(0,1), (1,2), (2,3), (3,0)},这是一个环。我们从节点0开始DFS。
- 访问节点0:
disc[0]=0,low[0]=0,父节点为-1(根节点)。 - 访问节点0的邻居1:
disc[1]=1,low[1]=1,parent[1]=0。 - 访问节点1的邻居2:
disc[2]=2,low[2]=2,parent[2]=1。 - 访问节点2的邻居3:
disc[3]=3,low[3]=3,parent[3]=2。 - 节点3的邻居是0和2。0已被访问且是父节点(2)的父节点,忽略;2是父节点,忽略。但节点3发现邻居0已被访问,且0不是其父节点(2),这是一条反向边。于是更新
low[3] = min(low[3], disc[0]) = min(3,0) = 0。 - 递归返回到节点2:更新
low[2] = min(low[2], low[3]) = min(2,0) = 0。 - 递归返回到节点1:更新
low[1] = min(low[1], low[2]) = min(1,0) = 0。 - 递归返回到节点0:更新
low[0] = min(low[0], low[1]) = min(0,0) = 0。 - 检查割点:
- 对于根节点0:它只有一个子节点(1),所以不满足
children >= 2,不是割点。 - 对于节点1:检查其子节点2,
low[2] = 0,disc[1] = 1,0 >= 1?不成立。所以节点1不是割点。 - 同理,节点2和3也不是割点。
- 对于根节点0:它只有一个子节点(1),所以不满足
- 结论:这个环图中没有割点。这符合直觉,因为在一个环中移除任何一个节点,剩下的节点依然通过环连接在一起。
总结:
通过一次DFS遍历,利用发现时间和low值,我们可以高效地找出无向图中的所有割点。关键点在于理解low值的含义以及两种割点的判断条件。