尋找圖的強連通分量:tarjan演算法簡單理解

Asterism發表於2019-07-04

1、簡介
tarjan是一種使用深度優先遍歷(DFS)來尋找有向圖強連通分量的一種演算法。

2、知識準備
棧、有向圖、強連通分量、DFS。

3、快速理解tarjan演算法的執行機制
提到DFS,能想到的是通過棧來儲存沿途的點,可以找到所有的環。環本身就是聯通的,所以環對於強連通分量來說環已經很接近最終答案了。要把找環變成找強連通管分量還要考慮:
a.在環外是不是有其他環在這個強連通分量內(極大性)

(會被認為是2個環)

b.一些不能構成環的點無法被考慮到,而他們本身就是強連通分量

(2不被認為是一個強連通分量)

 

所以Tarjan演算法除了棧還引入了2個陣列,分別是:
DFN[N]//節點的時間戳,用來標記節點訪問的先後順序(以及是否被訪問過)
Low[N]//當前“環”裡最先被訪問到的節點,相當於當前這個強連通分量裡的根


Tarjan的流程是:
DFS,每遇到一個未被訪問過的節點就初始化DFN[i]=Low[i]=index++;
如果找到了環,就在遍歷中用Low陣列向上傳遞根的時間戳,直到找到一個點他的時間戳和根的時間戳一致,即DFN[i]=Low[i],這就說明這個點就是根。此時,棧內的所有在根後面的點(包括根)就組成一個強連通分量。

4、虛擬碼

index=0;
tarjan(u)
{
    DFN[u]=low[u]=index++;
    u入棧;
    for(遍歷每條邊(u,v))
    {
        if(v未被訪問)
        {
            tarjan(v);//DFS
            low[u]=min(low(u),DFN(v));//將下方的時間戳向上傳遞
        }
        else if(v在棧內)
        {
            low[u]=min(low[u],DFN(v));//找到環,比較當前儲存的根的時間戳和v的時間戳,取較早的那個作為根
        }
        if(DFN(u)==low[u])
        {
            //回到了根節點,此時棧內從u往後的節點都是該強連通分量的節點
            //找到了強連通分量,逐個退棧,輸出
        }
    }
}


5、進一步說明
a.對於問題a,為什麼能找到強連通分量內其他的環?
DFS的問題在於,找到了環立即處理而不考慮其他環;Tarjan演算法把輸出交給根節點處理,在到根節點之前,演算法已經遍歷的根節點下的所有節點,自然也把所有環放入了棧。
b.對於問題b,為什麼考慮到了不能構成環的那些節點?
對於這些節點,DFN(u)==low[u],相當於他們本身就是強連通分量的根節點。

6、延伸閱讀
如果您仍然有疑問,可以參考https://blog.csdn.net/qq_34374664/article/details/77488976

相關文章