交互式图论学习
交互式图论学习
Guest User
Using app without sign in
强连通分量查找器
使用两次DFS遍历找到强连通分量
选择算法并生成步骤以开始可视化
Kosaraju 算法用两遍深度优先搜索计算有向图的强连通分量,一遍在原图上,一遍在其转置图上(所有边反向)。它在概念上是最简单的线性时间 SCC 算法。
第一遍 DFS 按完成时间递减记录顶点。随后把图转置,第二遍 DFS 按该顺序处理顶点;第二遍中生成的每棵树恰好是一个强连通分量。其正确性源于:反向边保持 SCC 不变,却切断了它们之间的连接。两遍线性扫描总计 O(V + E)。
Kosaraju 算法的应用与 Tarjan 相同:2-SAT 求解器、编译器分析、社交网络社区结构以及依赖凝聚。它的两遍结构更易讲解和从零实现,因此在被要求查找 SCC 时是常见的面试答案。
两次深度优先搜索加一张转置图。两趟之内都没有任何精巧之处,真正起作用的是第二趟运行时所依据的顺序。
Kosaraju(图):
// 第一趟:记录完成顺序
顺序 = []
对每个未访问的 u: dfs1(u)
dfs1(u): 标记 u 已访问
对每条边 (u,v): 若未访问则 dfs1(v)
顺序.追加(u) // 在完成时追加
// 第二趟:在转置图上按完成顺序的逆序做 DFS
gt = 转置(图) // 把每条边反向
对 逆序(顺序) 中的每个 u:
若 u 未访问:
在 gt 中从 u 生长出的树就是一个强连通分量它之所以成立:把所有边反向不会改变强连通分量,因为如果原先能从 x 走到 y 再走回来,反向之后依然可以。反向真正改变的是分量之间那些边的方向。从最后完成的结点开始,可以保证你起步于缩点图的一个源分量;反向之后它原本的出边变成了入边,于是第二趟搜索被困在该分量内部,无法溢出到别的分量去。
在与 Tarjan 示例完全相同的有向图上运行 Kosaraju,以便直接对照两者。
示例图: 有向边 A 到 B、B 到 C、C 到 A、B 到 D、D 到 E、E 到 D,以及 C 到 F。
得到的分量依次是 {A, B, C}、{D, E}、{F}。把它与同一张图上的 Tarjan 对照:后者输出的是 {F}、{D, E}、{A, B, C}。两者都正确,找到的也是同样的三个分量,但 Kosaraju 按缩点图的正向拓扑序输出,而 Tarjan 按逆序输出。如果你后续的代码在意这个顺序,这个差别就是二者取舍的理由。
时间: O(V + E) · 空间: O(V + E)
两次深度优先搜索各需 O(V + E),构造转置图还需要对所有边扫一遍,同样是 O(V + E)。三者相加仍为 O(V + E)。真正让 Kosaraju 输给 Tarjan 的是空间:它必须保存转置后的邻接结构,也就是边表的第二份完整副本,为 O(V + E);而 Tarjan 只需在原图之上额外记录 O(V) 的信息。在拥有数千万条边的图上,这个差别就是决定性的,这也是尽管两者时间复杂度完全相同、生产环境中通常仍选 Tarjan 的原因。
所有线性的强连通分量算法都是 O(V + E)。区别在于内存、遍历趟数,以及代码写对的难易程度。
| 替代算法 | 以下情况更合适 | 代价 |
|---|---|---|
| Tarjan 算法 | 一趟完成,无需转置图,额外空间只有 O(V)。在内存吃紧或图非常大时更可取。 | O(V + E),一趟 |
| 基于路径的强连通分量算法 | 同样一趟完成,但用两个显式栈代替 low-link 运算。有人觉得这样更好推理。 | O(V + E) |
| 缩点为 DAG | 分量只是手段而非目的。Kosaraju 交给你的顺序本来就是正向拓扑序。 | O(V + E) |
| 并查集 | 图是无向的,此时连通分量是一个简单得多的问题。 | O(E·α(V)) |