1. 程式人生 > >Tarjan算法【強連通分量】

Tarjan算法【強連通分量】

HR 得出 強連通 遍歷 str 時間復雜度 滿足 ack ID

轉自:byvoid:有向圖強連通分量的Tarjan算法

Tarjan算法是基於對圖深度優先搜索的算法,每個強連通分量為搜索樹中的一棵子樹。搜索時,把當前搜索樹中未處理的節點加入一個堆棧回溯時可以判斷棧頂到棧中的所有節點是否為一個強連通分量。

有兩個概念:1.時間戳,2.追溯值

時間戳是dfs遍歷節點的次序。

定義DFN(u)為節點u搜索的次序編號(時間戳),Low(u)為u或u的子樹能夠追溯到的棧中節點最小的次序號。由定義可以得出:

1 Low(u)=min{
2     DFN(u),   // 自己的次序號
3     Low(v),   //(u,v)為樹枝邊,u為v的父節點
4 DFN(v), //(u,v)為指向棧中節點的後向邊(非橫叉邊) 5 }

即以下節點的最小值:

1. 自己、子樹節點的次序號

2. 指向棧中節點(後向邊節點)的次序號[等價於 DFN(v)<DFN(u)且v不為u的父親節點],這裏不是橫叉邊(指向不在棧中的節點)。

DFN(u)=Low(u)時,以u為根的搜索子樹上所有節點是一個強連通分量。

偽碼:

 1 tarjan(u)
 2 {
 3     DFN[u]=Low[u]=++Index     // 為節點u設定次序編號和Low初值
 4     Stack.push(u)             //
將節點u壓入棧中 5 for each (u, v) in E // 枚舉每一條鄰邊 6 if (v is not visted) // 如果節點v未被訪問過 7 tarjan(v) // 繼續向下找 8 Low[u] = min(Low[u], Low[v])      9 else if (v in S) // 如果節點v還在棧內 10 Low[u] = min(Low[u], DFN[v]) 11 if (DFN[u] == Low[u]) //
如果節點u是強連通分量的根 12 repeat 13 v = S.pop // 將v退棧,為該強連通分量中一個頂點 14 print v 15 until (u== v) 16 }

運行Tarjan算法的過程中,每個頂點都被訪問了一次,且只進出了一次堆棧,每條邊也只被訪問了一次,所以該算法的時間復雜度為O(N+M)

一個頂點u是割點,當且僅當滿足(1)或(2)

(1) u為樹根,且u有多於一個子樹。

(2) u不為樹根,且滿足存在(u,v)為樹枝邊(或稱父子邊,即u為v在搜索樹中的父親),使得DFN(u)<=Low(v)。即:若某點的子樹們能回到的點大於等於自己,則該點為割點

一條無向邊(u,v)是,當且僅當(u,v)為樹枝邊,且滿足DFN(u)<Low(v)。

Tarjan算法【強連通分量】