算法介紹
- 學習記錄
- 關節點
- 雙連通圖
不包含關節點的圖,任何一個無向圖都可以視作由若干個極大的雙連通子圖組成
算法實現
可以利用深度優先算法實現,在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 = ++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;
}