Depth-First Search and Linear Graph Algorithms
一、首先给出一些定义:
- 树边:深度优先森林(无相连通图中深搜树T)中的边。
- 后向边:后向边(u,v)是将点u连接到其深度优先搜索树中的一个祖先节点v的边。
- Palm Tree(简称P):由树边和无向边以及这些点构成的图。
- 双连通图:设G = (V,E)为一无向连通图,对于V中的每三个点v,w,a,都有一条路径从v到w而不用经过a,我们就称G为双连通图。
- 关节点:设G中三点v,w,a,若任何从v到w的路径(至少有一条)都要经过a,我们就称a为G的一个关节点。
- 注意虽然双连通分量值针对于无向图,但是其DFS生成的P以及T都是有向图,T是P的子图。
于是有引理:设G = (V,E),若定义位于同一环路的边为等效边,则根据此将边进行划分Ei,边的端点集Vi,则我们称Gi = (Vi,Ei)为G的一个双连通分量。
双连通分量有以下性质:
- Gi之间互不包含。
- G中每个关节点至少出现一次在Vi中,每个非关节点都只出现在 一个Vi里面。
- Vi与Vj(i != j)交集最多只有一个元素,这个元素也就是图G的关节点。
二、双连通分量定理及实现
int BiConnect(short v,short u) //u is the father of v in spanning T
{
number[v] = low[v] = index++;
for(short w = 1;w <= N;w++)
{
if(w != u&&w != v&&graph[v][w]) //可行边并且不能是其本身以及其父节点
{
edge newEdge;
newEdge.x = v;
newEdge.y = w;
if(!number[w]) //w还没标记,则w将是v的一个子节点
{
edgeStack[stackTop++] = newEdge; //将边压入栈
BiConnect(w,v); //继续深搜
low[v] = min(low[v],low[w]); //深搜返回后调整low值
if(low[w] >= number[v]) //有双连通分量或者桥边
{
memset(bcc,0,sizeof(bcc));
while((--stackTop)&&(edgeStack[(stackTop)].x != newEdge.x||edgeStack[(stackTop)].y != newEdge.y)) //将直到(v,w)的边出栈
{
bcc[edgeStack[stackTop].x][edgeStack[stackTop].y] = 1; //将边保存到双连通分量bcc
}
bcc[edgeStack[stackTop].x][edgeStack[stackTop].y] = 1;
}
}
else if(number[w] < number[v]) //(v,w)是后向边,说明有环路
{
edgeStack[stackTop++] = newEdge;
low[v] = min(low[v],number[w]);
}
}
}
return 0;
}
- 在代码中,无向图以及双连通分量都是采用的矩阵表示。
- 根据tarjan算法,我们不断将遇到的树边以及后向边(只能是这两种边)压入栈,若遇到Low[w] >= number[v],则进行出栈以及双连通分量处理。注意此时的v不一定是关节点,(v,w)也不一定是桥(删除此边,图就不再连通)。
- 首先分析为什么不是么关节点,可以很容易举出反例,若G就是个单纯的环路,则DFS会一直往下走直到又回到根节点时,不等式等号成立,而根节点又显然不是关节点,单纯的环路图就没有关节点。这个与Tarjan的定理矛盾吗?当然是不矛盾的,注意定理题设中给出了三点,也即是必须还要有一点u不在v的生成树中,但是环路中,所有点(除了根节点)都在根节点的子树中了。所以求关节点时,代码中当v为根节点时,还要看DFS后子节点是否有超过两个,有的话则v是一个关节点(不同子树的点必须通过根节点连接)。
- 同样若G是单纯环路,则(v,w)显然也不是桥,注意到此时G是个双连通图,所有点的low值都等于number[root],所以不等式取等号说明(v,w)是双连通分量中的边,取大于号说明是桥。
338



被折叠的 条评论
为什么被折叠?



