Tarjan算法【強連通分量】
阿新 • • 發佈:2018-06-24
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算法【強連通分量】