有向圖中
, u可達
v不一定意味著v可達
u. 相互可達則屬於同乙個強連通分量
(strongly connected component, scc)
最關鍵通用部分:強連通分量一定是圖的深搜樹的乙個子樹。
1.演算法思路
基本思路:
這個演算法可以說是最容易理解,最通用的演算法,其比較關鍵的部分是同時應用了原圖
g和反圖gt。
(步驟1)先用對原圖
g進行深搜形成森林(樹
),(步驟
2)然後任選一棵樹對其進行深搜
(注意這次深搜節點
a能往子節點
b走的要求是
eab存在於反圖
gt),能遍歷到的頂點就是乙個強連通分量。餘下部分和原來的森林一起組成乙個新的森林,繼續步驟2直到
沒有頂點為止。
改進思路:
當然,基本思路實現起來是比較麻煩的
(因為步驟
2每次對一棵樹進行深蒐時,可能深搜到其他樹上去,這是不允許的,強連通分量只能存在單棵樹中
(由開篇第一句話可知
)),我們當然不這麼做,我們可以巧妙的選擇第二深搜選擇的樹的順序,使其不可能深搜到其他樹上去。想象一下,如果步驟
2是從森林裡選擇樹,那麼哪個樹是不連通(對於
gt來說
)到其他樹上的呢?就是最後遍歷出來的樹,它的根節點在步驟
1的遍歷中離開時間最晚,而且可知它也是該樹中離開時間最晚的那個節點。這給我們提供了很好的選擇,在第一次深搜遍歷時,記錄時間
i離開的頂點j,即
numb[i]=j
。那麼,我們每次只需找到沒有找過的頂點中具有最晚離開時間的頂點直接深搜(對於
gt來說
)就可以了。每次深搜都得到乙個強連通分量。
隱藏性質
:分析到這裡,我們已經知道怎麼求強連通分量了。但是,大家有沒有注意到我們在第二次深搜選擇樹的順序有乙個特點呢?如果在看上述思路的時候,你的腦子在思考,相信你已經知道了!!!它就是:如果我們把求出來的每個強連通分量收縮成乙個點,並且用求出每個強連通分量的順序來標記收縮後的節點,那麼這個順序其實就是強連通分量收縮成點後形成的有向無環圖的拓撲序列。為什麼呢?首先,應該明確搜尋後的圖一定是有向無環圖呢?廢話,如果還有環,那麼環上的頂點對應的所有原來圖上的頂點構成乙個強連通分量,而不是構成環上那麼多點對應的獨自的強連通分量了。然後就是為什麼是拓撲序列,我們在改進分析的時候,不是先選的樹不會連通到其他樹上(對於反圖
gt來說),也就是後選的樹沒有連通到先選的樹,也即先出現的強連通分量收縮的點只能指向後出現的強連通分量收縮的點。那麼拓撲序列不是理所當然的嗎?這就是
kosaraju
演算法的乙個隱藏性質。
2.偽**
kosaraju_algorithm:
step1
:對原圖
g進行深度優先遍歷,記錄每個節點的離開時間。
step2
:選擇具有最晚離開時間的頂點,對反圖
gt進行遍歷,刪除能夠遍歷到的頂點,這些頂點構成乙個強連通分量。
step3
:如果還有頂點沒有刪除,繼續
step2
,否則演算法結束。
3.實現**
#include
using namespace std;
const int maxn = 110;
typedef int adjtable[maxn]; //
鄰接表型別
int n;
bool flag[maxn]; //
訪問標誌陣列
int belg[maxn]; //
儲存強連通分量,其中
belg[i]
表%e
強聯通分量
include include include include include include include include using namespace std struct edge vectora 80005 b 80005 node 80005 int he 80005 int n,m,...
強聯通分量
2020 10 31 內容來自oi wiki和yu xuan 的講課 強聯通分量 個人理解 tarjan 演算法是由棧來實現的 dfn u dfs序 low u 以u為根的子樹,最小的dfs序那麼會出現 3 中情況 void tarjan int u else if vis v 1 low u mi...
強聯通分量 tarjan
tarjan演算法思想 dfs節點的時候,用time記錄訪問順序,則父節點會先於子節點訪問。那麼節點u遞迴的過程中找到了父節點 先訪問的 形成乙個環路,這個環路上的所有節點就是乙個強聯通分量。low的作用是用強聯通分量上的最先訪問的節點 訪問到的父節點 得time作為整個強聯通分量所有節點的時間。並...