Tarjan演算法求解一個無向圖中的割點和橋問題
阿新 • • 發佈:2019-01-09
基本概念
割點:Articulation Point
在無向連通圖中,刪除一個頂點v及其相連的邊後,原圖從一個連通分量變成了兩個或多個連通分量,則稱頂點v為割點,同時也稱關節點(Articulation Point)。
雙連通的圖:一個沒有關節點的連通圖稱為重連通圖(biconnected graph)(雙連通圖)。
連通度:k,若在連通圖上至少刪去k 個頂點才能破壞圖的連通性。
演算法應用
演算法應用:通訊網路中,用來衡量系統可靠性,連通度越高,可靠性越高。
求解方法
- 暴力求解,依次刪除每一個節點v,用DFS(或者BFS)判斷是否連通,再把節點加入圖中。若用鄰接表(adjacency list),需要做V次DFS,時間複雜度為O(V∗(V+E))O(V∗(V+E))
- Jarjan演算法,只用一次DFS求解。
第一個方法不多說:
對於第二個方法
我們要維持兩個資料結構:
dfn[u]:記錄節點u在DFS過程中被遍歷到的次序號。
low[u]:記錄節點u或u的子樹通過非父子邊追溯到最早的祖先節點(即DFS次序號最小)。
其中對於low[u]的理解是這樣的:當(u,v)為樹邊時,且v沒有被訪問過,則low[u]是min(low[u]和low[v])。當v被訪問過,如果v不是u的父節點(如果是則說明有重邊,不考慮),則(u,v)為回邊,則low[u]取min(low[u], dfn[v])。
程式碼思路解析
#include<iostream>
#include<vector>
#include<fstream>
using namespace std;
#define N 201
vector<int>G[N];
bool visit[N];
int dfn[N];
int low[N];
int parent[N];
int min(int a, int b)
{
if (a < b) return a;
else return b;
}
void input()
{
int n, m;//分別表示頂點數和邊數
ifstream in("input.txt");
in >> n >> m;
int a, b;
for (int i = 1; i <= m; ++i)
{
in >> a >> b;
G[a].push_back(b);/*鄰接表儲存無向邊*/
G[b].push_back(a);
}
}
void dfs(int u)
{
//記錄dfs遍歷次序
static int counter = 0;
//記錄節點u的子樹數
int children = 0;
visit[u] = true;
//初始化dfn與low
dfn[u] = low[u] = ++counter;
for (int j = 0; j < G[u].size(); j++)//遍歷與u相連的頂點
{
int v = G[u][j];
if (!visit[v])
{
children++;
parent[v] = u;//u是v的父節點
dfs(v);//深度優先搜尋v
low[u] = min(low[u], low[v]);//等v完成深度優先搜尋之後,low[u]記錄節點u或u的子樹通過非父子邊追溯到最早的祖先節點(即DFS次序號最小)
if (parent[u] == -1 && children > 1)//對根節點u,若其有兩棵或兩棵以上的子樹,則該根結點u為割點;
{
cout << "1 articulation point: " << u<<endl;
}
if (parent[u] != -1 && low[v] >= dfn[u])//對非葉子節點u(非根節點),若其子樹的節點均沒有指向u的祖先節點的回邊(條件low[v] >= dfn[u]表達的就是),說明刪除u之後,根結點與u的子樹的節點不再連通;則節點u為割點。
{
cout << "2 articulation point: " << u << endl; //這樣做,可能出現某個頂點多次輸出,其實可以用一個指示變數來指示,做到頂點不重複輸出
}
if (low[v] >dfn[u]) //橋的條件
{
cout << "Bridge " << v << " " << u << endl;
}
}
else if (v != parent[u]) {//節點v已訪問,則(u,v)為回邊,且不是重邊
low[u] = min(low[u], dfn[v]);
}
}
}
int main()
{
/*input();
memset(dfn, -1, sizeof(dfn));
memset(father, 0, sizeof(father));
memset(low, -1, sizeof(low));
memset(is_cut, false, sizeof(is_cut));
count();*/
memset(dfn, -1, sizeof(dfn));
memset(low, -1, sizeof(low));
memset(visit, false, sizeof(visit));
memset(parent, -1, sizeof(parent));
input();
dfs(1);
system("pause");
return 0;
}