1. 程式人生 > 實用技巧 >淺談Tarjan演算法

淺談Tarjan演算法

淺談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);
    }
}