---
# System prepended metadata

title: Algo｜Tarjan 演算法
tags: [Algo]

---

# Tarjan 演算法

## 無向圖，算 low 及 dfn，找割點與橋
- [1192. Critical Connections in a Network](https://leetcode.com/problems/critical-connections-in-a-network/)
- `dfn` : 每個點在 dfs 的順序
- `low` : 只用一個回邊，所能走到的最小 dfn 數值
    ```clike=
    vector<int> G[1000];
    int low[1000], dfn[1000], idx = 1;

    void dfs(int now, int parent){
        low[now] = dfn[now] = idx++;
        for(auto &nb:G[now]){
            if(nb == parent)    continue; //避免重複走同一個邊
            
            if(dfn[nb] == 0){ // tree edge
                dfs(nb, now);
                low[now] = min(low[now], low[nb]);
                // if(now != root && low[nb] >= dfn[now]) -> AP
                // if(low[nb] > dfn[now]) -> bridge
            } else {          // back edge
                low[now] = min(low[now], dfn[nb]);
            }
        }
        // if(now == root && child >= 2) -> AP
    }
    ```
- [點雙連通分量](https://ideone.com/7F3nvS) : 用割點分割圖，子圖中的點互相走的到
- [邊雙連通分量](https://ideone.com/wgh2S4) : 用橋分割圖，子圖中的點互相走的到
## 有向圖，找強連通分量(SCC)
- 有向圖，[強連通分量](https://ideone.com/e2Zfxh)的點互相走的到
- 方法一: `Tarjan's Algorithm`
    - 透過 `Stack` 紀錄正在處理的 vertex
    - 透過 `inStack` 排除 `forword edge` 及 `cross edge`
    ```clike=
    void dfs(int now){
        low[now] = dfn[now] = idx++;
        Stack.push(now);
        inStack[now] = true;
        for(auto &nb:G[now]){
            if(dfn[nb] == 0){         // tree edge
                dfs(nb);
                low[now] = min(low[now], low[nb]);
            } else if (inStack[nb]) { // back edge
                low[now] = min(low[now], dfn[nb]);
            }
        }

        if(low[now] == dfn[now]) {
            while (Stack.top() != now){
                int tmp = Stack.top();  Stack.pop();
                BCC[bcnt].push_back(tmp);
                inStack[tmp] = false;
            }
            bcnt++;
        }
    }
    ```
- 方法二: `Kosaraju Algorithm`
    - 先對反向圖跑一次 DFS，紀錄 `離開 DFS 的時間`
    - 由離開時間大到小對原圖跑 DFS，走的到的地方形成 `SCC`
    ```clike=
    void rdfs(int now){
        rDone[now] = true;
        for(auto &nb:rG[now])
            if(!rDone[nb])
                rdfs(nb);
        order.push_back(now);
    }

    void dfs(int now){
        done[now] = true;
        BCC[bcnt].push_back(now);
        for(auto &nb:G[now])
            if(!done[nb])
                dfs(nb);
    }

    void Kosaraju(){
        for(int i=0;i<n;i++)
            if(!rDone[i])
                rdfs(i);

        reverse(order.begin(),order.end());

        for(auto i:order){
            if(!done[i]){
                bcnt++;
                dfs(i);
            }
        }
    }
    ```
