割点:如果在图G中删去一个结点u后,图G的连通分枝数增加,即W(G-u)>W(G),则称结点u为G的割点,又称关节点。
桥:如果在图G中删去一条边e后,图G的连通分支数增加,即W(G-e)>W(G),则称边e为G的桥,又称割边或关节边。
双连通分支:G中不含割点的极大连通子图称为G的双连通分支,又称为G的块。
例子:给定一个无向图,找出图中的割点和桥
说明:节点用v表示
Vis[v]:记录节点v当前的访问状态:1表示在栈中,0表示未
访问,2表示已经访问过
Dfn[v]:记录节点v被访问时的深度
Low[v]:记录节点v可以到达的访问时间最早的祖先
在深度遍历图的过程中,记录下每个节点的深度,对当前节点cur,以及和它相连的点i,有两种情况:
(1)i没有·被访问过,这时递归访问节点i,并用i可以到达的最早的祖先来更新cur的low值。
(2)i在栈中这是说明图中有一个环,用i的深度更新cur的low值。
cur是割点的条件:cur是根且有大于一个的儿子,或者cur不是根,且cur有一个儿子v,使得low[v]>=dfn[cur]
(cur,i)是桥的条件:low[i]>dfn[cur]
void CutBridge(int cur, int father, int dep, int n){
//可设为全局变量
const int V = 1000;
int edge[V][V];
int bridge[V][V], cut[V];
int low[V], dfn[V], vis[V];
vis[cur] = 1; dfn[cur] = low[cur] = dep;
int children = 0;
for (int i = 0; i < n; ++i)if (edge[cur][i]){
if (i != father && 1 == vis[i]){
if (dfn[i] < low[cur])
low[cur] = dfn[i];//用i被访问时的深度来更新cur的low值
}
if (0 == vis[i]){
CutBridge(i, cur, dep + 1, n);//递归访问节点i
children++;
if (low[i] < low[cur])low[cur] = low[i];
if ((father == -1 && children > 1) || (father != -1 && low[i] >= dfn[cur]))
cut[cur] = true;
if (low[i] > dfn[cur]){ bridge[cur][i] = bridge[i][cur] = true; }
}
}
vis[cur] = 2;
}