tarjan算法總結

部份內容引自https://www.cnblogs.com/stxy-ferryman/p/7779347.htmlhtml

Tarjan算法不是一個算法而是一類算法算法

1.求取強連通份量數組

  強連通份量————有向圖的強連通子圖優化

  tarjan算法基於dfs,利用棧的思想,把下面全部的點都遍歷完畢後,所能連接的最小祖先節點(可能沒有),就是要尋找的強連通份量htm

  因此咱們須要dfn數組存儲dfs的遍歷順序,low數組存儲這個節點後全部的子孫節點所能到達的最小節點(dfn最小)值blog

  爲了可以得知構成這個強連通份量的全部的點,能夠利用棧去記錄,由於退的時候,確定是退到最頭上(若是有額外的分支,那麼以前確定早就退棧了)繼承

  當咱們遍歷的時候初始化時dfn = low = idx這個初始化意思很好懂,若是沒有後繼節點,值就是這個遞歸

  接下來咱們可能會面對三種點get

  ·沒有遍歷過的,咱們就遞歸tarjan遍歷,而後優化low[u] = min(low[u],low[v])class

  ·咱們遍歷過了,這個點還在棧中,那就表明這個點u能夠到達,因此咱們更新的時候,low[u] = min(low[u],dfn[v])_____你要知道此時的low[v]是取決於如今的low[u]的由於v在棧中,因此u是v的後繼節點,這是一條返祖邊

  ·咱們遍歷過了,這個點不在棧中了,證實這個點經歷了一次退棧,造成了一次聯通份量,可是不包括u,由於這個點不能到達u,若是這個點能夠到達u的話,u又能夠到達這個點,那麼就不會退棧了(這裏的到達都是要通過子孫節點的),因此對於這樣的點,沒必要考慮任何問題

 因此直到遍歷完全部的子孫節點咱們就能夠進行退棧的操做了,那些獨立的聯通份量的標誌就是

  low == dfn

  意思就是對於節點u,其子孫節點所能到達的最大節點就是u,也就是造成了一個迴路,環,並且能夠保證這個環是最大的

因此說了這麼多,以上的算法思想用於求取一個圖的強連通份量——最後進行color染色處理

void tarjan(int u,int fa)
{
    dfn[u] = low[u] = ++index;
    stk[s_cnt++] = u;
    instk[u] = true;
    
    for(int i = id[u];~i;i = e[i].pre)
    {
        int v = e[i].to;
        if(!dfn[v])
        {
            tarjan(v,u);
            low[u] = min(low[u],low[v]);
        }
        else if(instk[v] == true)
        {
            low[u] = min(low[u],dfn[v]);
        }
    }
    if(dfn[u] == low[u])
    {
        col++;
        while(s_cnt > 0 && stk[s_cnt] != u)
        {
            s_cnt --;
            color[stk[s_cnt]] = col;
            instk[stk[s_cnt]] = false;
        }
    }
}

 2.tarjan縮點

利用tarjan算法能夠把一個圖變成單向無環圖

這個繼承自強連通份量,對於每個強連通份量,咱們可以看出一個超級點,這個超級點的內部能夠互相到達,而後根據這個圖所表示的含義,一般要去計算超級點的度,最後輸出知足題意的超級點內全部的點

和上面的代碼一致

3·求割點和橋(割邊)

割點:去掉這個點(和這個點外射的全部邊),把一個連通圖變成多個連通份量

割邊:一樣的道理,去掉這條邊,把一個連通圖變成多個連通份量

這時候咱們要判斷什麼時候是一個割點

  1.當前節點是根節點——從這個節點開始的dfs,因此若是這個點有兩個子樹,那麼就是一個割點

  2.任意節點,若是low[v] >= dfn[u],表示u的這個子孫可以到達的最小祖先節點是比u小的,因此u是子孫v鏈接祖先的關鍵點

void tarjan_gedian(int u,int fa)
{
    int son = 0;
    dfn[u] = low[u] = ++index;
    for(int i = id[u];~i;i = e[i].pre)
    {
        int v = e[i].to;
        if(!dfn[v])
        {
            tarjan(v,u);        son++;
            low[u] = min(low[u],low[v]);
            if(u != root && low[v] >= dfn[u])
            {
                cut_point[u] = 1;
            }
            else if(u == root && son > 1)
            {
                cut_point[u] = 1;
            }
        }
        else if(v != fa)對於割點u這是一條迴路,且u割去以後毫無影響,因此忽略這樣的兩點間迴路狀況
        {
            low[u] = min(low[u],dfn[v]);
        }
    }
}
相關文章
相關標籤/搜索