C++圖論強連通分量講解

前言:

強連通分量好強,老師好喜歡()。

概念:

在有向圖G中,如果兩點互相可達,則稱這兩個點強連通,如果G中任意兩點互相可達,則稱G是強連通圖。

    1、一個有向圖是強連通的,當且僅當G中有一個迴路,它至少包含每個節點一次。

    2、非強連通有向圖的極大強連通子圖,稱爲強連通分量。

在這張圖中,{1,2,3,4}是一個強聯通分量{5},{6}分別是另外兩個強聯通分量。

實現方法:

1.Kosaraju算法

我們先來看這樣一張圖,這是一個有兩個強聯通分量的一個有向圖。如果我們對這個有向圖進行遍歷,我們如果是從A這邊開始,那麼只需要一個DFS就可以搜索完整個有向圖,但我們如果從B這邊開始,則需要兩個DFS。很顯然,我們這個有向圖有兩個強聯通分量,那麼,我們選擇合適的起始位置(B這邊的點)才能得到正確的答案。

可是我們該如何實現呢?

1、隨意給起點進行DFS(一般從1開始),每次在DFS回溯的過程中進行標記,得到一個後序遍歷的順序的表。

2、對原圖G進行反向,得到反圖R,按照步驟1中得到的表,從後往前進行DFS遍歷。

3、在步驟2中,使用了多少次的DFS,就有多少個強聯通分量。

想一想,步驟2就是反着走能夠回到目標節點,那麼這就是一個強連通分量呀。

所以使用了多少次的DFS,就有多少個強聯通分量。

2.Tarjan算法(推薦)

Tarjan算法是基於DFS的算法,每個強連通分量爲搜索樹中的一棵子樹。搜索時,把當前搜索樹中未處理的節點加入一個堆棧,回溯時可以判斷棧頂到棧中的節點是否爲一個強連通分量。

定義DFN[u]爲節點u的搜索次序編號,Low[u]爲u或u的子樹能追溯到的最早的棧中節點。初始化時,DFN[u]=Low[u]=編號

將搜索到的u壓入棧中,枚舉所有u出去的邊,如果v沒有被訪問,那麼繼續找,並在回溯時,計算Low[u]=min(Low[u],Low[v]),如果v在棧內,那麼Low[u]=min(Low[u],DFN[v]).當u沒有下一個節點可以搜索時,判斷DFN[u]是否等於Low[u],如果相等,則將v退棧,直到退出u爲止,這次退出的所有點即爲一個連通分量。

對於最開始那個圖, 我們從1這個點開始,一直搜索到6,這時,DFN[6]=Low[6],且沒有可以繼續深入的點,那麼,退出直到u=v爲止,這時,棧內退出6,即{6}是一個強連通分量。

接下來,返回到節點5,發現DFN[5]=Low[5],且5沒有其他的路徑搜索,那麼,同樣將棧內的點退出,直到u=v,得到{5}是一個強聯通分量。

接下來返回3點,然後繼續搜索4點,把4加入棧,發現4有一條邊連向1,且1已在棧中,所以Low[4]=1,6已經出棧,返回3,此時,3沒有其他的點可以繼續搜索,更新Low[3]=Low[4]=1.

繼續返回節點1,發現可以繼續訪問節點2,那麼,訪問2,發現2還有另外一條邊連向4,發現4還在棧中,更新Low[2]=DFN[4]=5,返回1。此時,1已經沒有其他可以搜索的點了,那麼,把節點出棧,直到u=v爲止,此時出棧的這些點{1,3,4,2}組成一個強連通分量。

而爲什麼我要推薦這個算法呢?

主要是時間複雜度的問題,第一個方法的時間複雜度爲點數加邊數*2,而第二個爲點數加邊數。

大數據之後優勢就明顯了。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章