在有向圖g中,如果兩個頂點間至少存在一條路徑,稱兩個頂點強連通(strongly connected)。如果有向圖g的每兩個頂點都強連通,稱g是乙個強連通圖。非強連通圖有向圖的極大強連通子圖,稱為強連通分量(strongly connected components)。
下圖中,子圖為乙個強連通分量,因為頂點1,2,3,4兩兩可達。,也分別是兩個強連通分量。
直接根據定義,用雙向遍歷取交集的方法求強連通分量,時間複雜度為o(n^2+m)。更好的方法是kosaraju演算法或tarjan演算法,兩者的時間複雜度都是o(n+m)。本文介紹的是tarjan演算法。
tarjan演算法是基於對圖深度優先搜尋的演算法,每個強連通分量為搜尋樹中的一棵子樹。搜尋時,把當前搜尋樹中未處理的節點加入乙個堆疊,回溯時可以判斷棧頂到棧中的節點是否為乙個強連通分量。
定義dfn(u)為節點u搜尋的次序編號(時間戳),low(u)為u或u的子樹能夠追溯到的最早的棧中節點的次序號。由定義可以得出,
low(u)=min
當dfn(u)=low(u)時,以u為根的搜尋子樹上所有節點是乙個強連通分量。
演算法偽**如下
tarjan(u)
接下來是對演算法流程的演示。
從節點1開始dfs,把遍歷到的節點加入棧中。搜尋到節點u=6時,dfn[6]=low[6],找到了乙個強連通分量。退棧到u=v為止,為乙個強連通分量。
返回節點5,發現dfn[5]=low[5],退棧後為乙個強連通分量。
繼續回到節點1,最後訪問節點2。訪問邊(2,4),4還在棧中,所以low[2]=dfn[4]=5。返回1後,發現dfn[1]=low[1],把棧中節點全部取出,組成乙個連通分量。
至此,演算法結束。經過該演算法,求出了圖中全部的三個強連通分量,,。
可以發現,執行tarjan演算法的過程中,每個頂點都被訪問了一次,且只進出了一次堆疊,每條邊也只被訪問了一次,所以該演算法的時間複雜度為o(n+m)。
求有向圖的強連通分量還有乙個強有力的演算法,為kosaraju演算法。kosaraju是基於對有向圖及其逆圖兩次dfs的方法,其時間複雜度也是o(n+m)。與trajan演算法相比,kosaraju演算法可能會稍微更直觀一些。但是tarjan只用對原圖進行一次dfs,不用建立逆圖,更簡潔。在實際的測試中,tarjan演算法的執行效率也比kosaraju演算法高30%左右。此外,該tarjan演算法與求無向圖的雙連通分量(割點、橋)的tarjan演算法也有著很深的聯絡。學習該tarjan演算法,也有助於深入理解求雙連通分量的tarjan演算法,兩者可以模擬、組合理解。
求有向圖的強連通分量的tarjan演算法是以其發明者robert tarjan命名的。robert tarjan還發明了求雙連通分量的tarjan演算法,以及求最近公共祖先的離線tarjan演算法,在此對tarjan表示崇高的敬意。
附:tarjan演算法的c++程式
void tarjan(int i)
else
if (instack[j] && dfn[j]
low[i]=dfn[j];
}if (dfn[i]==low[i])
while (j!=i);
}}void
solve()
強連通分量 tarjan求強連通分量
雙dfs方法就是正dfs掃一遍,然後將邊反向dfs掃一遍。挑戰程式設計 上有說明。雙dfs 1 include 2 include 3 include 4 include 5 6using namespace std 7const int maxn 1e4 5 8 vector g maxn 圖的鄰...
強連通分量
對於有向圖的乙個頂點集,如果從這個頂點集的任何一點出發都可以到達該頂點集的其餘各個頂點,那麼該頂點集稱為該有向圖的乙個強連通分量。有向連通圖的全部頂點組成乙個強連通分量。我們可以利用tarjan演算法求強連通分量。define n 1000 struct edge e 100000 int ec,p...
強連通分量
include include include using namespace std typedef struct link graphiclink graphic,plink graphic typedef struct queuequeue void init queue queue 佇列初始...