算法介绍
- 学习记录
- 关节点
- 双连通图
不包含关节点的图,任何一个无向图都可以视作由若干个极大的双连通子图组成
算法实现
可以利用深度优先算法实现,在DFS搜索过程中记录和更新u所能联通的最高祖先(经后向边), hca(u) < dTime(v) 则说明u可以联通v的真祖先,v不是关节点;否则v是关节点
算法源码
// 基于DFS的双连通分量分解算法
void bcc(int s) {
// 重置图
reset();
// 时间标记
int clock = 0;
int v = s;
// 栈记录已访问节点
Stack<int> S;
// 栈存储关节点
Stack<int> cut_vertex;
// 遍历各个节点
do {
// 没有访问过的顶点执行BCC函数
if (status(v) == UNDISCOVERED) {
BCC(v, clock, S, cut_vertex);
// 清空栈S, 进入下一次循环
while (!S.empty()) {
S.pop();
}
}
// 切换到下一个顶点
v = % n;
} while (s != v);
}
// 基于DFS的双连通分量分解算法
void BCC(int v, int &clock, Stack<int> &S, Stack<int> &cut_vertex) {
// 当前顶点状态调整
status(v) = DISCOVERED;
// 时间标记 hca(v)标记的顶点v可以触达的最早的祖先的dTime dTime(v)访问时间
hca(v) = dTime(v) = ++clock;
// 顶点入栈
S.push(v);
//遍历各个邻接顶点
for (int u = firstNbr(v); u > -1; u = nextNbr(v, u)) {
switch (status(u)) {
// 未发现
case UNDISCOVERED:
// 更新遍历树
parent(u) = v;
type(v, u) = TREE;
// 递归执行BCC
BCC(u, clock, S, cut_vertex);
// u可以触达v的祖先,非关节点,此时v也可以通过后向边触达这个祖先
if (hca(u) < dTime(v)) {
hca(v) = min(hca(u), hca(v));
} else {
// 此时v是关节点, u一下就是一个双连通域
cut_vertex.push(v);
// TODO 存储双连通域
while(v = S.pop());
// 关节点重新入栈,分摊不足一次
S.push(v);
}
break;
case DISCOVERED:
// 后向边
type(v, u) = BACKWARD;
// 更新v节点可以触达的最早的祖先的dTime(u)
if (u != parent(v)) {
hca(v) = min(hca(v), dTime(u));
}
break;
case VISITED: // 有向边专属
type(v, u) = dTime(v) < dTime(u) ? FORWARD : CROSS;
break;
}
}
// 更新当前顶点的状态
status(v) = VISITED;
}