1. 程式人生 > 其它 >【演算法模板】tarjan

【演算法模板】tarjan

Tarjan演算法基於深度優先搜尋, 每個強連通分量為搜尋樹中的一棵子樹. 搜尋時, 把當前搜尋樹中未處理的結點加入堆疊, 回溯時就可判斷棧頂到棧中的結點是否為一個強連通分量.
dfn[u]: 記錄結點u在DFS過程中被遍歷到的次序號(時間戳).
對於一棵DFS樹, 對於原圖中的非樹邊, 為便於描述, 定義:
前向邊: 祖先->兒子的邊.
後向邊: 兒子->祖先的邊.
橫叉邊: 沒有祖先兒子關係的邊(注意橫叉邊
只會往dfn減小的方向連線).
low[u]: 記錄結點u或u的子樹能夠追溯到的dfn最小的值(棧中標號的最小點)

由定義, 顯然有:
⚫ 若(u,v)為樹邊, low[u] = min{dfn[u], low[v]};
⚫ 若(u,v)是指向棧中結點的後向邊, low[u] = dfn(v).
定理: 當dfn[u] = low[u]時, 以u為根的搜尋子樹上所有結點構成一個強連通分量.
證明: dfn[u]表示u點被DFS到的時間, low[u]表示u和u所有的子樹所能到達的點中dfn最小值.
dfn[u]=low[u], 這說明u點及u的子樹結點最多隻有指向u點的邊, 而沒有指向u的祖先的邊了.
顯然, 遍歷過的結點從u出發又最終回到u形成一個環, 即u點與它的子孫結點構成了強連通分量.

inline void paint(int x) {
    s.pop(); ins[x] = 0;
    color[x] = colorNum;
    colorSize[colorNum]++;
}

void tarjan(int u) {
    dfn[u] = low[u] = ++dfnn;
    s.push(u);
    ins[u] = vis[u] = 1;

    for (int i = 0; i < g[u].size(); i++) {
        int v = g[u][i];
        if (!dfn[v]) {  //前向邊
            tarjan(v);
            low[u] = min(low[u], low[v]);
        }
        else if (ins[v])    //後向邊(排除橫叉邊)
            low[u] = min(low[u], dfn[v]);
    }
    if (dfn[u] == low[u]) {
        colorNum++;
        while(s.top() != u) {
            int x = s.top();
            paint(x);
        }
        paint(u);
    }
}