Kosaraju算法是求解有向圖強連通份量(strong connected component)的三個著名算法之一,能在線性時間求解出一個圖的強份量。用Sedgewick爺爺的話說,就是「現代算法設計的勝利」。 算法
什麼是強連通份量?在這以前先定義一個強連通性(strong connectivity)的概念:有向圖中,若是一個頂點s到t有一條路徑,t到s也有一條路徑,即s與t互相可達,那麼咱們說s與t是強連通的。那麼在有向圖中,由互相強連通的頂點構成的份量,稱做強連通份量。 編程
首先說一些離散數學相關的結論,由強連通性的概念能夠發現,這是一個等價關係。 數組
證實: spa
一,按照有向圖的約定,每一個頂點都有到達自身的路徑,即自環,即任意頂點s到s可達,知足自反性; 設計
二,若是s與t是強連通的,則s到t存在路徑,t到s存在路徑,顯然t與s也是強連通的,知足對稱性; component
三,若是r與s強連通,s與t強連通,則r與s互相可達,s與t互相可達,顯然r與t也互相可達,知足傳遞性。 排序
所以,強連通關係可導出一個等價類,這就是強連通份量。進一步的利用這結論能夠知道,兩個強連通份量之間木有交集(這個結論很重要)。事實上,圖論與離散數學中的關係有很是密切的……關係。 數學
在編程求解強連通份量時,一般作法是對頂點進行編號,擁有相同編號的頂點屬於同一個強連通份量。在求解完以後,經過對編號的比較能夠迅速判斷兩個頂點是不是強連通的。 it
------------------------------分割線----------------------------------- 搜索
Kosaraju算法過程上並不複雜。要求解一個有向圖的強連通份量,第一步:在該圖的逆圖上運行DFS,將頂點按照後序編號的順序放入一個數組中(顯然,這個過程做用在DAG上獲得的就是一個拓撲排序);第二步:在原圖上,按第一步得出的後序編號的逆序進行DFS。也就是說,在第二次DFS時,每次都挑選當前未訪問的結點中具備最大後序編號的頂點做爲DFS樹的樹根。
Kosaraju算法的顯著特徵是,第一,引用了有向圖的逆圖;第二,須要對圖進行兩次DFS(一次在逆圖上,一次在原圖上)。並且這個算法依賴於一個事實:一個有向圖的強連通份量與其逆圖是同樣的(即假如頂點任意頂點s與t屬於原圖中的一個強連通份量,那麼在逆圖中這兩個頂點一定也屬於同一個強連通份量,這個事實由強連通性的定義可證)。因爲算法的時間取決於兩次DFS,所以時間複雜度,對於稀疏圖是O(V+E),對於稠密圖是O(V²),可見這是一個線性算法。Kosaraju的結論是,在第二次DFS中,同一棵搜索樹上的結點屬於一個強連通份量。
證實:假設頂點s與t屬於第二次DFS森林(注意,第二次是在原圖上搜索)的同一棵樹,r是這棵樹的根結點。那麼有如下兩個事實:一,原圖中由r可達s,這蘊含在逆圖中從s到r有一條路徑;二,r在逆圖中的後序編號大於s(r是樹根,所以r的後序編號比樹中全部的其餘結點的都大)。如今要證實的是在逆圖中從r到s也是可達的。
好,兩個事實結合起來:一,假設逆圖中r到s不可達,且s到r存在路徑,那麼這條路徑將使s的後序編號比r大,與事實一矛盾,排除;二,假設逆圖中r到s存在路徑,正是這條r到s的路徑使得r有更大的後序編號,則r與s是強連通的,假設成立(看上去比較勉強,我的認爲這應該是一個空證實)。顯然,兩個事實導出一個結論:逆圖中,r與s互相可達。同理,r與t也互相可達,根據傳遞性,第二次DFS森林中同一棵樹中的全部頂點構成一個強連通份量。
另外一方面,會不會一個強連通份量的全部頂點沒有出如今第二次DFS森林的同一顆樹中呢?答案是:不會。由於根據DFS的性質,若是r與s強連通,那麼由r開始的DFS一定能搜到s。
證畢。
可見Kosaraju的方法可以找出有向圖的強連通份量,那麼爲何這個方法可行呢?或者如何實現呢?這正是Kosaraju算法最爲精妙的地方,關鍵在於第二次DFS選取的順序:在第一次DFS中,將頂點按照後序編號存放,第二次DFS就按照這個順序的逆序進行搜索,這保證每次選取的根結點(剛纔證實中的r結點)都具備未訪問結點中最大的後序編號,則搜索中拓展的結點的後序編號都比根結點小,這樣也就知足了事實二。
補充:Kosaraju算法雖然是線性的,可是須要兩次DFS,跟另外兩個著名的求解強份量的算法相比,這是一個劣勢。可是Kosaraju算法有個神奇之處在於:計算以後的強份量編號的順序,恰好是該有向圖K(D)(kernel DAG, 核心DAG)的一個拓撲排序!所以Kosaraju算法同時提供了一個計算有向圖K(D)拓撲排序的線性算法。這個結果在一些應用中很是重要。