Kosaraju算法、Tarjan算法分析及证明--强连通分量的线性算法
一、背景介紹
強連通分量是有向圖中的一個子圖,在該子圖中,所有的節點都可以沿著某條路徑訪問其他節點。強連通性是一種非常重要的等價抽象,因為它滿足
- 自反性:頂點V和它本身是強連通的
- 對稱性:如果頂點V和頂點W是強連通的,那么頂點W和頂點V也是強連通的
- 傳遞性:如果V和W是強連通的,W和X是強連通的,那么V和X也是強連通的
強連通性可以用來描述一系列屬性,如自然界中物種之間的捕食關系,互相捕食的物種可以看作等價的,在自然界能量傳遞中處于同一位置。
下圖中,子圖{1,2,3,4}為一個強連通分量,因為頂點1,2,3,4兩兩可達。{5},{6}也分別是兩個強連通分量。
二、Kosaraju算法描述
Kosaraju算法通過以下步驟獲得一個有向圖的強連通分量。
- 在圖G中,計算圖G的反向圖G'的深度優先搜索的逆后序排列。反向圖是比如原圖中有V到W的鏈接,那么反向圖中就有W到V的鏈接。逆后序排列是指,在對一個圖進行深度優先遍歷時,先進行子節點的遞歸,再將本節點加入到一個棧中,最后依次出棧以獲得的序列。逆后序排列有一個重要特性,就是如果有W到V的有向鏈接,那么實際出棧時,W仍然排在V的前面。
- 在G中進行普通的深度優先搜索,但是搜索順序不是按照正統的( i = 0, i < N )去依次搜索,而是以剛剛獲得的逆后序排列的順序進行搜索。
- 每個以這個逆后序排列中的元素開始的DFS搜索,找到的所有元素,都是同一個強聯通分量的元素。
為什么這個算法可以獲得強連通分量呢?網上的證明很少,所以下面給出我的邏輯證明。
三、Kosaraju算法證明
我們按照算法描述的步驟往下走:
四、算法源碼
?因為代碼很長,放在github上了。代碼是在Idea中編譯運行通過的,實現了一個基本的Graph數據結構,在此基礎上實現了Kosaraju類,供參考。
源文件
五、更加迅速的tarjan算法
部分內容轉自https://www.byvoid.com/blog/scc-tarjan
1.Tarjan算法
Tarjan算法是基于對圖深度優先搜索的算法,每個強連通分量為搜索樹中的一棵子樹。搜索時,把當前搜索樹中未處理的節點加入一個堆棧,回溯時可以判斷棧頂到棧中的節點是否為一個強連通分量。
定義DFN(u)為節點u搜索的次序編號(時間戳),Low(u)為u或u的子樹能夠追溯到的最早的棧中節點的次序號。由定義可以得出,
Low(u)=Min {DFN(u),Low(v),(u,v)為樹枝邊,u為v的父節點DFN(v),(u,v)為指向棧中節點的后向邊(非橫叉邊) }當DFN(u)=Low(u)時,以u為根的搜索子樹上所有節點是一個強連通分量。
算法偽代碼如下
tarjan(u) {DFN[u]=Low[u]=++Index // 為節點u設定次序編號和Low初值Stack.push(u) // 將節點u壓入棧中for each (u, v) in E // 枚舉每一條邊if (v is not visted) // 如果節點v未被訪問過tarjan(v) // 繼續向下找Low[u] = min(Low[u], Low[v])else if (v in S) // 如果節點v還在棧內Low[u] = min(Low[u], DFN[v])if (DFN[u] == Low[u]) // 如果節點u是強連通分量的根 repeatv = S.pop // 將v退棧,為該強連通分量中一個頂點 print vuntil (u== v) }2.算法的精要之處如下:
- 為每個加入的節點設定序號,使得后搜索到的節點的序號一定高于前面的節點
- 那么,如果后搜索到的節點的子節點里居然有比它本身還要小的節點,則一定出現了環。有環則必定強連通
- 那么,把該節點的標識節點Low(u)設為發現的后向節點的值DFN(V)
- 然后遞歸程序返回該節點的上級節點u-1,上級節點判斷Low(u-1)的值,也把它指向了剛剛找到的后向節點
- 最后,除了后向節點本身,所有環中的節點都指向該后向節點,那么,我們找到了一個強連通分量。
3.算法流程的演示
從節點1開始DFS,把遍歷到的節點加入棧中。搜索到節點u=6時,DFN[6]=LOW[6],找到了一個強連通分量。退棧到u=v為止,{6}為一個強連通分量。
返回節點5,發現DFN[5]=LOW[5],退棧后{5}為一個強連通分量。
返回節點3,繼續搜索到節點4,把4加入堆棧。發現節點4向節點1有后向邊,節點1還在棧中,所以LOW[4]=1。節點6已經出棧,(4,6)是橫叉邊,返回3,(3,4)為樹枝邊,所以LOW[3]=LOW[4]=1。
繼續回到節點1,最后訪問節點2。訪問邊(2,4),4還在棧中,所以LOW[2]=DFN[4]=5。返回1后,發現DFN[1]=LOW[1],把棧中節點全部取出,組成一個連通分量{1,3,4,2}。
至此,算法結束。經過該算法,求出了圖中全部的三個強連通分量{1,3,4,2},{5},{6}。
可以發現,運行Tarjan算法的過程中,每個頂點都被訪問了一次,且只進出了一次堆棧,每條邊也只被訪問了一次,所以該算法的時間復雜度為O(N+M)。
求有向圖的強連通分量還有一個強有力的算法,為Kosaraju算法。Kosaraju是基于對有向圖及其逆圖兩次DFS的方法,其時間復雜度也是O(N+M)。與Trajan算法相比,Kosaraju算法可能會稍微更直觀一些。但是Tarjan只用對原圖進行一次DFS,不用建立逆圖,更簡潔。在實際的測試中,Tarjan算法的運行效率也比Kosaraju算法高30%左右。此外,該Tarjan算法與求無向圖的雙連通分量(割點、橋)的Tarjan算法也有著很深的聯系。學習該Tarjan算法,也有助于深入理解求雙連通分量的Tarjan算法,兩者可以類比、組合理解。
?
轉載于:https://www.cnblogs.com/bethunebtj/p/4854946.html
總結
以上是生活随笔為你收集整理的Kosaraju算法、Tarjan算法分析及证明--强连通分量的线性算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 天猫app接入饿了么 为用户提供更好的
- 下一篇: 查看文档(API) (NSString)