客戶端基本不用的算法系列:Tarjan 算法求解強連通份量
在 《Tarjan 算法的思路》中咱們已經給出了 Tarjan 算法中的比較重要的幾個元素,咱們在這裏從新複習一下:算法
DFN[]
數組:數組存儲訪問順序,也就是遍歷的點會分配一個序號(從小到大),而後序號存在這個數組當中:DFN[u]
爲節點 u 搜索的次序編號;數組LOW[]
數組:表示該點能直接或間接到達時間最小的頂點。例如:LOW[u]
爲節點 u 的子樹可以追溯到最先的棧中節點的次序號;微信stack
存儲該連通子圖中的全部點。app
下面咱們來聊一聊這幾個東西要怎麼用。spa
什麼是強連通份量
咱們先給出一個強連通份量的定義:在有向圖 G 中,若是兩個頂點 u, v 之間存在一條 u 到 v 的路徑,也存在一個 v 到 u 的路徑,則稱這兩個頂點 u, v 是強連通的(strongly connected)。若是有向圖 G 的任意兩個頂點都強連通,則稱 G 是一個強連通圖。.net
非強連通圖有向圖的極大強連通子圖,稱爲強連通份量(strongly connected components)。3d
下圖中,子圖{1,2,3,4}爲一個強連通份量,由於頂點1,2,3,4兩兩可達。{5},{6}也分別是兩個強連通份量,總共三個強連通份量。code
算法過程
咱們先給出一個演示 Tarjan 算法的經典圖,從根結點 1 開始DFS,把遍歷到的節點入棧(1-3-5-6)
,當搜索到 u=6
的時候,DFN[6] = LOW[6]
,當 DFN == LOW
的時候,咱們認爲找到一個強連通份量。而後執行彈棧操做,直到 u == v
爲止,{6}
爲一個強連通份量。component
DFN = 4,LOW = 4
。
以後回溯到 5 節點,發現
DFN[5] == LOW[5]
,同 6 節點同樣繼續進行彈棧操做,
{5}
爲一個強連通份量。
回溯到結點 3,繼續 DFS 搜索到結點 4,把 4 進棧。這時發現節點 4 向節點 1 有後向邊,節點 1 還在棧中。因此 LOW[4] = 1
。因爲節點 6 已經彈棧, 邊 (4, 6)
是指向非棧中節點的橫叉邊,所以不更新 LOW[4]
。回溯返回到結點3,(3, 4)
爲樹枝邊,因此 LOW[3] = LOW[4] = 1
。orm
橫叉邊:從某個結點指向搜索樹中另外一個子樹中的某結點的邊
樹枝邊:DFS 時通過的邊,即 DFS 搜索樹上的邊。
回溯到根結點 1,最後 DFS 到點 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)
。
這篇文章至此,下一篇咱們開始聊聊如何實現 Tarjan 算法,以及在結題中如何套用 Tarjan 算法模板來求解題目。最後,若是你以爲單看文不夠生動的話,點擊查看原文還會有大佬的視頻講解(原來電子科大的 qscqesze 大神,如今個人同事😁 )
本文分享自微信公衆號 - 一瓜技術(tech_gua)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。