淺談Tarjan演算法
阿新 • • 發佈:2020-12-01
淺談Tarjan演算法
本篇隨筆簡單講解一下Tarjan縮點演算法。Tarjan演算法支援很多東西,什麼橋割點之類的,這裡只介紹縮點的那個Tarjan。
一、前置知識和概念
Tarjan縮點的學名其實是Tarjan演算法求強連通分量。那麼介紹這些概念:
強連通:如果兩個頂點可以相互通達,則稱兩個頂點 強連通(strongly connected)。如果有向圖G的每兩個頂點都 強連通,稱G是一個強連通圖。非 強連通圖有向圖的極大強連通子圖,稱為強連通分量(strongly connected components)。
來個經典圖,大多數人講Tarjan都會用到的圖:
在這張圖中,1234就是個強連通分量。
二、Tarjan演算法的原理
很多部落格上都只著重講了Tarjan的實現,並沒有太過於講解其原理上的東西。那我來補充一下,因為我水平實在是有限,所以如果有的說的不對的敬請指正。
強連通分量是啥啊,那不是兩兩可達麼。
既然可達,那對其進行遍歷的時候肯定會重複通過啊。那中間它怎麼來的就是和它在一個強連通分量裡的唄。
那麼我們用DFS+一個棧來維護這個過程。
兩個陣列:一個dfn是時間戳。如果不知道時間戳是啥,戳這裡:時間戳,一個low表示目前在棧裡的所有節點中時間戳最小的時間戳,換句話說,是i或i的子樹能夠追溯到的最早的棧中節點的時間戳。
整個演算法的執行過程是對整張圖進行深度優先遍歷的過程。當回溯時dfn[x]==low[x]時,以之為根的子搜尋樹的所有節點可以被構成一個強連通分量。
比如上面的那張圖,我們可以手動模擬這個過程。
三、Tarjan演算法的程式碼實現
程式碼:
void tarjan(int x) { dfn[x]=low[x]=++cnt; st[++top]=x; v[x]=1; for(int i=head[x];i;i=nxt[i]) { int y=to[i]; if(!dfn[y]) { tarjan(y); low[x]=min(low[x],low[y]); } else if(v[y]) low[x]=min(low[y]); } if(low[x]==dfn[x]) { int now; do { now=st[top--]; v[now]=0; }while(now!=x); } }