關節點算法

所謂關節點就是在一個連通圖中刪除某個頂點及其附屬的邊,原圖變爲非連通圖。從任一點出發深度優先遍歷得到優先生成樹,對於樹中任一頂點V而言,其孩子節點爲鄰接點。由深度優先生成樹可得出兩類關節點的特性:   

(1)若生成樹的根有兩棵或兩棵以上的子樹,則此根頂點必爲關節點。因爲圖中不存在連接不同子樹頂點的邊,若刪除此節點,則樹便成爲森林。   

(2)若生成樹中某個非葉子節點V,其某棵子樹與V的祖先節點無連接,則V爲關節點。因爲刪去v,則其子樹和圖的其它部分被分割開來

用圖說明一下吧:

如上圖所示,很顯然去掉A或C後整個圖就變成不聯通了,因此A、C就是關節點。怎麼用代碼實現找出關節點呢?其實我們可以嚴格按照上面的兩條性質用dfs搜索樹上來實現。首先,我們假設搜索順序是A B C D E F,那麼我們可以畫出一棵搜索樹:

我們先來看性質(1),根A有2棵子樹分別爲B,D-C-E-F。因此A必定爲關節點,因爲各個子樹不可能存在一條到達A的祖先(A沒有祖先)的回邊構成一條迴路。所以在以某一條邊開始搜索到最後搜索完畢時,需要查看根的子樹的個數,若子樹小於2則該點不是關節點,反之則是關節點。

假如搜索順序爲:D C A B F E,當前節點是A,A的兩棵子樹分別爲B,F-E;因爲E-F子樹存在一條回邊c-e(用小寫字母表示吧)導致A有一條可以到達祖先的迴路,此時我們並不能確定A是不是關節點,因爲還有一顆子樹B沒有檢測,然而A並沒有一條回邊,當我們去掉A後,它的子樹B就與它的祖先失去“聯繫”,因此A就是關節點。

那麼我們怎麼確定一條回邊呢?我們可以在dfs的過程中將搜索序列號記錄下來存進pre[]內,在搜索每一條路徑結果若到達一個節點序號已經存在而且比自己的搜索序列號小,我們可以將該序列號記錄下來存進low[]中作爲某一個節點通過某一條路徑所能到達的最小序列值。在每一個子樹回溯給該節點所能到達的最小值時,我們先判斷該值是否小於該節點的序列號,若小於,那麼該節點可以通過該子樹到達自己的祖先,我們在將該節點的low[]更新。否則,該子樹是一棵真正存在於該節點下的一顆子樹。

總結以上陳述也就是說,搜索每一個節點通過自己的各個子樹所能到達的最小序列號是否都比自己的序列號小,若成立則是關節點,否則不是。

若以臨接表作爲數據結構,代碼可以寫成如下形式:

複製代碼
int dfs(int i,int root)//但前節點及其父節點
{
int j,w,k=0;
if(pre[i]==0) pre[i]=low[i]=++cnt;
for(j=head[i];j;j=edge[j].next)
{
w=edge[j].i;
if(pre[w]==0)
{
dfs(w,i);
if(low[i]>low[w]) low[i]=low[w];//更新
if(low[w]>=pre[i]) k++;
}
else if(w!=root)
{
if(low[i]>pre[w]) low[i]=pre[w];
}
}
if(k) count++;//關結點個數加一
return k;
}
for(i=1;i<=n;i++)
{
if(pre[i]==0)
{
k=dfs(i,0);
if(k<2) count--;
}
}
複製代碼

爲什麼if(low[w]>=pre[i]) k++; 中需要用k++呢,爲什麼搜索接收後有一個if(k<2) count--;

if(low[w]>=pre[i]) k++;中的k表示有幾個不能夠到達祖先的子樹,有人說將k可變爲bool變量,其實k的值在搜索過程中確實可以換成bool,但是搜索結束後,我們需要用到這個值,這也是性質(1)決定的,根節點的所有子節點序列值不可能小於根節點,因此搜索完畢後根節點肯定作爲一個關節點來算,因此我們需要重新檢測一下根節點到底有幾棵子樹,此時k只就派上用場了。

有人會在if(low[w]>=pre[i])犯糊塗,爲什麼是>=而不是>呢?那好我們看下下面一個圖:

假如搜索序列是A B C E D,而且if(low[w]>pre[i]),我們畫出搜索樹:

若當前節點爲B,則B有兩棵子樹C,E-D;B可以通過子樹C到達祖先A,而B通過子樹E-D所獲的最小序列號是B的序列號,若按if(low[w]>pre[i])那麼B就不是關節點,這就錯了。對於根A來說它只有一棵子樹,當然不是關節點。有人會說拓撲圖是如下這樣,若按照if(low[w]>=pre[i])不也錯了嗎。

其實不然,假如說搜索順序爲A B C D ;我們之前說過整個搜索完成之後對根做一次再檢測,顯然A不會是關節點。


本文轉載自:http://www.cnblogs.com/yu-chao/archive/2011/12/17/2291403.html

發佈了12 篇原創文章 · 獲贊 16 · 訪問量 21萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章