1. 程式人生 > 其它 >Kosaraju 求強連通分量

Kosaraju 求強連通分量

感覺比 Tarjan 好寫多了!雖然正確性可能不如 Tarjan 好理解。

先求出 dfs 樹,然後按照出棧序倒序在反圖上 dfs,每次 dfs 所有能走到的點都構成了一個強連通分量,然後將它們在圖上刪去。

程式碼(來自 oi-wiki

時間複雜度 \(\mathcal{O}(|V|+|E|)\)

// C++ Version
// g 是原圖,g2 是反圖

void dfs1(int u) {
  vis[u] = true;
  for (int v : g[u])
    if (!vis[v]) dfs1(v);
  s.push_back(u);
}

void dfs2(int u) {
  color[u] = sccCnt;
  for (int v : g2[u])
    if (!color[v]) dfs2(v);
}

void kosaraju() {
  sccCnt = 0;
  for (int i = 1; i <= n; ++i)
    if (!vis[i]) dfs1(i);
  for (int i = n; i >= 1; --i)
    if (!color[s[i]]) {
      ++sccCnt;
      dfs2(s[i]);
    }
}

正確性的話,感性理解一下:

考慮縮點後的 DAG,對於一個 SCC,顯然能找到它的所有節點,證明其不會走到其它 SCC 的節點即可:

  • 它不會走到它的前驅 SCC 以外的 SCC:因為是按照反圖上沒有到達其的邊;

  • 它不會走到它的前驅 SCC:因為它的前驅 SCC 出棧序比它大,已經被標記過了。