圖之強連通、強連通圖、強連通分量 Tarjan演算法
一、解釋
在有向圖G中,如果兩個頂點間至少存在一條互相可達路徑,稱兩個頂點強連通(strongly connected)。如果有向圖G的每兩個頂點都強連通,稱G是一個強連通圖。非強連通圖有向圖的極大強連通子圖,稱為強連通分量(strongly connected components)。
求解有向圖的強連通分量演算法有很多,例如Kosaraju,Gabow和Tarjan演算法,其中Gabow和Tarjan演算法時間複雜度要優於Kosaraju。
理解:
如果單純將其看出圖的話有點難以理解,但是當我們將其看成樹,就很容易了。
如上圖,如果兩個點成強聯通,那麼顯然在樹中就會存在一個環,圖中L-M-J-L和A-L-M-B-A成環所以組成的強聯通分量。
二、Tarjan演算法
Tarjan演算法基於深度優先搜尋樹,其有兩個重要變數DFN[u]:表示在深度搜尋中遍歷到該節點的次序。LOW(u)表示以u節點為樹根,u及u以下樹節點所能找到的最小次序號。注意Tarjan認為單個節點自身就是一個強聯通分量,在處理資料時注意遮蔽。以上圖為例,我們從A開始,
A:DFN[1] = 1; LOW(1)=1
L:DFN[2] = 2; LOW(2)=2
M:DFN[3] = 3; LOW(3)=3
J:DFN[4] = 4; LOW(4)=4
這時我們在J節點繼續往下搜尋時,發現L節點我們已經搜尋過了,且L:LOW(2)=2,我們發現J:LOW(4)=4>L:LOW(2)=2,因此我們將其賦值LOW(4)=2,這說明此時我們發現了一個環,代表一個強聯通分量。
下面繼續:
J:DFN[4] = 4; LOW(4)=2
M:DFN[3] = 3; LOW(3)=2
B:DFN[5] = 4; LOW(5)=5
發現B到A:
B:DFN[5] = 4; LOW(5)=1
開始返回更新:
M:DFN[3] = 3; LOW(3)=1
L:DFN[2] = 2; LOW(2)=1
A:DFN[1] = 1; LOW(1)=1
發現DFN=LOW(1),彈出棧。
演算法:
void tarjan(int u){
DFN[u]=LOW[u]=++time; //次序從1開始,初始時由於預設將DFN[u]=LOW[u]都置為次序號
// 將當前節點壓棧,置位在棧中,已訪問。
visit[u]=1;
s.push(u);
instack[u]=1;
//取u節點的下一路徑節點v,當沒有v可取時也說明深度搜尋已經到達當前最底部,這是我們函式返回尋找另一條路徑。
for(int j=0;j<G[u].size();j++){
int v=G[u][j];
if(visit[v]==0){
tarjan(v);
// 在深度搜尋返回時,如果v節點下存在子樹,要將u節點的LOW[u]更新。
LOW[u]=min(LOW[u],LOW[v]);
}
else if(instack[v]){
// v節點已經被訪問,並且在棧中,說明在當前路徑上存在環,此處只是賦值,但並不代表在u子樹的底下的多個節點沒有比當前環更大的環。無法作為深度終止條件。
LOW[u]=min(LOW[u],DFN[v]);
}
}
int m;
int num=0; //對一個環計數計數
// 在深度搜尋完結後返回時,判斷DFN[u]==LOW[u],相等說明找到了一個環,將棧中節點彈出。注意tarjan演算法認為單個節點也為環。
if(DFN[u]==LOW[u]){
// 將棧中節點彈出,並計數
do{
m=s.top();
s.pop();
instack[m]=0;
num++;
}while(m!=u);
// 只有環內節點數大於兩個才是真正環。
if(num>1){
// n個點兩兩相交(互相到達),則有n*(n-1)/2條連線線
total+=num*(num-1)/2;
}
}
}
關於為啥只用訪問一次:
開始疑惑,肯定會多條路徑通過某一點,如果用visit記錄訪問記錄的話,下一條路徑不就會不能訪問該點了嗎?遂繪製醜圖:
如圖當我們訪問到6節點時發現有環,且到達底點,這時根據演算法開始返回,同時將2-6-5這條環也遍歷掉(此時5號已訪問壓棧且有LOW=1)。也就是說在返回到1號節點開始出棧時,我們已經把1號節點的子樹全部訪問了一遍,該成環的也做了標記。在1號節點下的子節點不會通向1號節點以上的節點,比如0號節點,不然1號只能算一個類似於2-6-5這條環。至於從0號到5號就不用再判斷了。所以遍歷一遍就行。我覺得巧妙之處在於在深度向前搜尋過程並沒有處理資料,而在深度返回過程中開始更新資料,記錄找到的迴路,並且到達子樹根節點DFN[u]==LOW[u]才開始出棧。
演算法例項:
CCF 高速公路
相關文章
- 圖論——強連通分量(Tarjan演算法)圖論演算法
- 強連通分量(Tarjan演算法)演算法
- Tarjan 求有向圖的強連通分量
- Tarjan求強連通分量
- Tarjan演算法(強連通分量分解)演算法
- 有向圖的連通性(判強連通)
- 連通圖與Tarjan演算法演算法
- 尋找圖的強連通分量:tarjan演算法簡單理解演算法
- 【模板】tarjan 強連通分量縮點
- 無向連通圖點雙連通分量
- 無向連通圖邊雙連通分量
- kosaraju 和 tarjan演算法詳解(強連通分量)演算法
- 抓間諜(強連通)
- 強連通分量
- 有向圖的強連通分量 模版
- 連通圖演算法詳解之① :Tarjan 和 Kosaraju 演算法演算法
- 強連通------tarjan演算法詳解及與縮點聯合運用演算法
- The Bottom of a Graph-POJ2553強連通
- 【演算法學習】tarjan 強連通、點雙、邊雙及其縮點 重磅來襲!!!!演算法
- UVA-11504 - Dominos(有向圖的強連通分量)
- 20行程式碼實現,使用Tarjan演算法求解強連通分量行程演算法
- UVA1327 && POJ1904 King's Quest(tarjan+巧妙建圖+強連通分量+縮點)
- 強聯通分量tarjan
- POJ 1236 Network of Schools 強連通分量
- 最大半連通子圖
- SSL_1758【連通圖】
- 有向圖強連通分支
- 有向圖的強連通性(java)Java
- 強連通分量及縮點 演算法解析及例題演算法
- 【Tarjan SCC 加邊使得所有圖聯通 至少選取多少個點能圖聯通 】Network of Schools加強版.md
- Day7 割點、割邊和強連通分量
- 無向連通圖求割點和橋
- 1852 連通塊
- 最小連通代價
- 前端如何通過連結去下載img圖片前端
- P2272 [ZJOI2007] 最大半連通子圖
- 邊分治維護強連通分量(CF1989F,P5163)
- 通達信強烈反轉主圖指標公式原始碼指標公式原始碼
- Laravel 通過 ODBC 連線 VerticaLaravel