強連通分量的三種演算法和四種實現

2021-06-18 23:43:30 字數 2513 閱讀 1182

常見的(我見過的)強連通分量的三種演算法有:1. 

kosaraju演算法

(雙dfs

)2.tarjan

演算法 3.gabow

一.kosaraju演算法

演算法的核心實現是,首先dfs

一遍,得到乙個

dfs森林,在此過程中得到所有點的拓撲序列(按結束時間由高到低),之後我們建乙個反向圖,按反拓撲序(結束時間由高到低)進行第二次

dfs,則此時得到的每一棵樹都是乙個強連通分量,這個畫個圖演示一下比較好理解,嚴格證明還是參考演算法導論

340頁較好,感性的認識是,假定我們有c1,

c2兩個強連通分量,而在反拓撲序中

c1是在

c2前面的,此時說明

g中第一遍

dfs時先結束了c2,

c1才結束的,假設

c2中的點是從

c1可達的,也就是在第一遍

dfs時

c2中點的全部時間戳的區間位於

c1的區間內,則反向時,這時當將所有邊反轉時

c1就沒有邊能到

c2了(前提是他倆確實是

scc),按照先c1在

c2的順序

dfs就行了,這裡用到了一些

dfs的性質,看演算法導論為好。如果它們本來就是兩棵樹那麼飯拓撲序靠前的那棵也不可能有邊到後面的。雖然本演算法慢,但是它得到了乙個連通分量的拓撲序,有時用的上。

二.tarjan演算法

tarjan演算法時間效率比

kosaraju

好,因為演算法中只使用了一次

dfs,實現的思想是,我對每個點規定

3個屬性

tim[i]

和low[i]

和vst[i], tim

和dfs

中的時間戳類似,就是只記錄到達時間,

low反映的是當前點通過自己的非樹邊(回退邊)或子樹中的非樹邊(回退邊),最多能連到離根最近的點(本點須為當前點的直系祖先或當前點),在

dfs到此點初始化時令

tim[v] = low[v] =  ++ times

,這樣當

dfs完乙個點的子樹之後,發現

tim[v]

仍然和low[v]

相等,就說明這個點和它的子樹中的點組成了乙個強連通分量了,在這個過程中我們可以用乙個棧來輔助,在

dfs乙個點時就壓入這個點,同時令標記

instack[i] = true

,這樣我們可以通過

stack

來判斷v

點鄰接中的已訪問過的點是不是

v的直系祖先(是直系祖先才能構成環)。在

dfs完乙個點後,判斷

tim[v]

是否還與

low[v]

相等,相等的話說明這個點與他的子樹構成了乙個強連通分量。彈出

stack

中的點,直到本點,賦予他們同乙個強連通分量標號,同時令

instack[v] = false;

我自己利用並查集實現了一下tarjan

演算法,過程類似於用並查集實現雙連通分量,不過因為是無向圖,當前的一條分枝可能還是會指向原來已經

dfs過的

sbling

旁支,這時我們的解決方案是用

dfs的雙時間標號標記起始和終止時間,根據

dfs標號的區間性質就可以判

v鄰接的乙個已訪問過的點是否是直系祖先了。並查集的實現過程是一旦

son沒被訪問且

low[son] >= tim[father],merge

兩者。 三.

gabow

其實如果說能把gabow

也單列出來作為一種演算法,我那個也能算一種了,區別在於我的比

tarjan

原版還慢,而

gabow

好像快一點,

gabow

演算法使用兩個棧來維護,

stk1

和原來的一樣,

stk2

則用來求強連通分量構成子樹的根節點,插入過程

stk1,stk2

和tarjan

中的一樣,遇到乙個插入乙個,而當有乙個點的鄰接點是他的祖先在棧中我們就彈出

stk2

中的點直到

stk2[top] == 

這個鄰接的點,這時剩下的這個點很有可能是乙個強連通分量的根節點,

dfs完乙個點後,看

stk2[top]

是否等於當前節點,若等於同樣說明當前點所有子樹中點最多回退到當前點,此時構成了乙個強連通分量,

stk2

中彈出這個點,

stk1

操作與tarjan

中相同,對每個標記強連通分量標號即可。這樣的好處是不用頻繁修改

low的值了,好像能快一點,實現也沒比

tarjan

複雜,基本一樣。

hoj 2741實測結果

1.gabow: 0.19s

2.tarjan:

0.20s

3.tarjan + union-find set :

0.71s

強連通分量的三種演算法

常見的 我見過的 強連通分量的三種演算法有 1.kosaraju演算法 雙dfs 2.tarjan演算法 3.gabow 一.kosaraju演算法 演算法的核心實現是,首先dfs一遍,得到乙個dfs森林,在此過程中得到所有點的拓撲序列 按結束時間由高到低 之後我們建乙個反向圖,按反拓撲序 結束時間...

強連通分量的Kasaraju演算法的實現

其中 1595446 節點個數 0 起始邊 1 終邊 base.h pragma once include tree.h class cbase base.cpp include stdafx.h include base.h cbase cbase ctree t,int l cbase cbas...

模板 強連通分量的kosaraju演算法實現

n頭牛,現在給出m組關係 a,b 表示a喜歡b,喜歡具有傳遞性,問有多少頭牛是被所有牛喜歡。include include include include include include include include include include using namespace std cons...