Kosaraju 求強連通分量
阿新 • • 發佈:2022-01-27
感覺比 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 出棧序比它大,已經被標記過了。