Kosaraju演算法解決強連通問題

2021-05-27 18:35:21 字數 2474 閱讀 2511

什麼是強連通分量?在這之前先定義乙個強連通性(strong connectivity)的概念:有向圖中,如果乙個頂點s到t有一條路徑,t到s也有一條路徑,即s與t互相可達,那麼我們說s與t是強連通的。那麼在有向圖中,由互相強連通的頂點構成的分量,稱作強連通分量

首先說一些離散數學相關的結論,由強連通性的概念可以發現,這是乙個等價關係

證明:

一,按照有向圖的約定,每個頂點都有到達自身的路徑,即自環,即任意頂點s到s可達,滿足自反性;

二,如果s與t是強連通的,則s到t存在路徑,t到s存在路徑,顯然t與s也是強連通的,滿足對稱性;

三,如果r與s強連通,s與t強連通,則r與s互相可達,s與t互相可達,顯然r與t也互相可達,滿足傳遞性。

因此,強連通關係可匯出乙個等價類,這就是強連通分量。進一步的利用這結論可以知道,兩個強連通分量之間木有交集(這個結論很重要)。事實上,圖論與離散數學中的關係有非常密切的……關係。

在程式設計求解強連通分量時,通常做法是對頂點進行編號,擁有相同編號的頂點屬於同乙個強連通分量。在求解完之後,通過對編號的比較可以迅速判斷兩個頂點是否是強連通的。

------------------------------昏鍋線-----------------------------------

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)拓撲排序的線性演算法。這個結果在一些應用中非常重要。

最後附上我的實現~就一目了然啦~

---------------------------昏鍋線again--------------------------------

// kosaraju演算法鄰接矩陣實現

static int cnt, cntr, pre[maxv], postr[maxv];

int kosaraju(graph g) 

} return cnt; 

// 返回強連通分量的個數

}

void dfspostr(graph g, int v) 

void dfssc(graph g, int v) 

int graphsc(graph g, int s, int t) 

強連通分量 Kosaraju演算法

求有向圖的強連通分量除了大家熟知的trajan,還可以用kosaraju 先說演算法流程 1,對原圖dfs一遍,並將出棧順序的逆序作為 偽拓撲序 2,對原圖夠構反向圖 3,按偽拓撲序在反向圖上dfs,新遍歷到的點都屬於同乙個強聯通分量。正確性證明 s在反向圖上dfs能夠遍歷到t,說明存在t到s的路徑...

強連通分量Kosaraju演算法

參考閱讀 如何理解kosaraju演算法?簡緻的回答 知乎 kosaraju演算法步驟 用dfs後序遍歷原圖的反轉圖,得到後序遍歷順序 用得到的後序遍歷順序對原圖dfs 實現 棧和圖的定義 const int maxv 100 定義棧 typedef struct snode stack 定義邊 t...

Kosaraju演算法 強連通分量

有向圖的極大強連通子圖,稱為強連通分量。子圖指的是選取v的乙個子集v 以及e當中所有滿足u,v v 的邊集e 所指代的圖.我們需要找出一幅有向圖當中的所有強連通分量。乙個最樸素的演算法 構造乙個傳遞閉包 也就是陣列aij表示i能否到達j 然後把aij aji 1的節點置於同乙個強連通分量當中 這個演...