強連通分量及縮點tarjan演算法解析

九野的部落格發表於2013-11-16


強連通定義:在有向圖G<V,E>中,對於點集V'∈V, 點集中的任意兩點都可達,則稱V'為強連通。

 

孤立的一個點也是一個強連通分量

 

在巢狀的多個環時 : {所有環上的點}為一個強連通分量( 最小環就是每個孤立點)注意一定是滿足條件的最大點集

 則上圖中強連通分量有 {1},{2},{3},{7},{4,5,6}

---------------------------------------------------------------------------

tarjan的過程就是dfs過程

對圖dfs一下,遍歷所有未遍歷過的點 ,會得到一個有向樹,顯然有向樹是沒有環的。(注意搜過的點不會再搜)

能產生環的 只有(指向已經遍歷過的點)的邊

如左圖,只有紅色與綠色邊有可能產生環。

對於深搜過程,我們需要一個棧來儲存當前所在路徑上的所有點(棧中所有點一定是有父子關係的

再仔細觀察紅邊與綠邊,首先得到結論:紅邊不產生環,綠邊產生環

1、對於紅邊,連線的兩個點3、7沒有父子關係,這種邊稱為橫叉邊。

橫叉邊一定不產生環。

2、對於綠邊,連線的兩個點6、4是父子關係,這種邊稱為後向邊。

環一定由後向邊產生。

3、圖中除了黑色的樹枝邊,一定只有橫叉邊和後向邊(不存在其他種類的邊)

-------------------------------------------------------------------------

則以下考慮對於這兩種邊的處理和判斷:

首先深搜會搜到這樣的圖:

Stack = {1,2,3},3沒有多餘的其他邊,因此3退棧,把3作為一個強連通分量

-------------------------------------------------------------------------

再次深搜:

此時棧 Stack = {1,2,7}

發現紅邊指向了已經遍歷過的點3 => 是上述的2種邊之一

而3不在棧中 => 3點與7點無父子關係

=> 該邊為橫叉邊

=>採取無視法。

繼而7點退棧 產生連通分量{7}

繼而2點退棧 產生連通分量{2}

--------------------------------------------------------------------------------------

再次深搜:

此時 Stack = {1,4,5,6}

發現綠邊指向了已經遍歷過的點4 => 是上述的2種邊之一

而4在棧中 => 4點與6點是父子關係

=> 該邊為後向邊

=>4->6的路徑上的點都是環。

int num[N], Top = 0;
int u = Stack.top(); 
while(u!=4){ num[Top++] = u; Stack.pop(); u = Stack.top();}
num[Top++] = u;

如此就能把Stack中 4->6路徑上的點轉移到num陣列裡

顯然num陣列中的點是一個連通分量。


-------------------------------------------------------------------------

實際情況可能更復雜:


出現了大環套小環的情況,顯然我們認為最大環是一個強連通分量(即:{4,5,6,8} )

因而我們需要強化一下dfs過程:

定義:

int Time, DFN[N], Low[N];

DFN[i]表示 遍歷到 i 點時是第幾次dfs

Low[u] 表示 以u點為父節點的 子樹 能連線到 [棧中] 最上端的點 的DFN值(換句話說,是最小的DFN,因為最上端的DFN是最小的嘛)

 

int Stack[N], top; //上述的棧


具體過程詳見模版


相關文章