算法复习——图算法篇之强连通分量
以下内容主要参考中国大学MOOC《算法设计与分析》,墙裂推荐希望入门算法的童鞋学习!
1. 问题背景
-
社交圈划分
- 如何把人群按通话记录划分成不同的社交圈?
- 如果闭环通话,说明关系相对密切;如果单向通话,说明关系相对疏远。
-
强连通分量(有向图)
- 一个强连通分量是顶点的子集
- 强连通分量中任意两点相互可达
- 满足最大型:加入新顶点,不保证相互可达
- 特性:任意两强连通分量不相交
- 反证易得,若相交,破坏了最大性
2. 问题定义
强连通分量(Strongly Connected Components)
输入:
- 有向图 G = < V , E > G=<V, E> G=<V,E>
输出:
- 图的所有强连通分量 C 1 , C 2 , … , C n C_1, C_2, \dots, C_n C1,C2,…,Cn
3. 算法框架
步骤1:把边反向,得到反向图 G R G^R GR
步骤2:在 G R G^R GR上执行DFS,得到顶点完成时刻顺序 L L L
例如,得到的 L L L如下所示:
步骤3:在 G G G上按 L L L逆序执行DFS,得到强连通分量
4. 伪代码
Strongly-Connected-Component(G)
输入:图G
输出:强连通分量
R ← {}
G_R ← G.reverse()
L ← DFS(G_R)
color[1..V] ← WHITE
for i ← L.length() downto 1 do
u ← L[i]
if color[u] = WHITE then
L_scc ← DFS-Visit(G, u)
R ← R ∪ set(L_scc)
end
end
return R
DFS(G)
输入:图G
现金数组color[1..V],L[1..V]
for v ∈ V do
color[v] ← WHITE
end
for v ∈ V do
if color[v] = WHITE then
L' ← DFS-Visit(G, v)
向L结尾追加L'
end
end
DFS-Visit(G, v)
输入:图G,顶点v
输出:按完成时刻从早到晚排列的顶点L
color[v] ← GRAY
for w ∈ G.Adj[v] do
if color[w] = WHITE then
L ← DFS-Visit(G, w)
end
end
color[v] ← BLACK
向L结尾追加结点v
return L
该算法的时间复杂度是 O ( ∣ V ∣ + ∣ E ∣ ) O(|V|+|E|) O(∣V∣+∣E∣)。
5. 算法正确性证明
- 强连通分量图 G S C C G^{SCC} GSCC:把强连通分量看作一个点,得到有向图
-
性质: G S C C G^{SCC} GSCC一定是有向无环图
-
反证:若存在环,两强连通分量中顶点相互可达,与最大性矛盾
-
S C C S i n k SCC_{Sink} SCCSink: G S C C G^{SCC} GSCC中出度为0的点
- 性质1: G S C C G^{SCC} GSCC中至少存在一个 S C C S i n k SCC_{Sink} SCCSink
- 性质2:删除 S C C S i n k SCC_{Sink} SCCSink,会产生新的 S C C S i n k SCC_{Sink} SCCSink
- 反证:若不存在,所有点均有出度,必存在环;而无环图子图必无环,矛盾
结合性质观察算法第2次DFS,第2次DFS恰恰是按照 S C C S i n k SCC_{Sink} SCCSink的顺序搜索,每次搜索恰好访问该SCC的所有点。
那么问题就是为什么通过按 L L L逆序执行DFS实现,就可以保证第2次DFS按照 S C C S i n k SCC_{Sink} SCCSink的顺序搜索且每次搜索恰好访问该SCC的所有点?
- 给定反向图 G R G^R GR,存在边 ( u , v ) (u, v) (u,v), u ∈ S C C 1 u \in SCC_1 u∈SCC1, v ∈ S C C 2 v \in SCC_2 v∈SCC2(在原始图 G G G中, S C C 1 SCC_1 SCC1为 S C C s i n k SCC_{sink} SCCsink)
-
若先搜索 u u u,会从 u u u搜索 v v v,则 f ( v ) < f ( u ) f(v)<f(u) f(v)<f(u)
-
若先搜索 v v v, v v v搜索完成才开始搜索 u u u,则 f ( v ) < f ( u ) f(v)<f(u) f(v)<f(u)
-
可知在 L L L( G R G^R GR上DFS完成时刻顺序)中 v v v一定在 u u u前面
-
所以按 L L L的逆序,总是先搜索 u u u,符合 S C C s i n k SCC_{sink} SCCsink的顺序
-
因此我们就证明了按 L L L逆序执行DFS实现确实可以保证第2次DFS按照 S C C s i n k SCC_{sink} SCCsink的顺序搜索。
- 在强连通分量内,顶点相互可达 → DFS可以访问到该 S C C SCC SCC所有点
- S C C s i n k SCC_{sink} SCCsink出度为0 → DFS不会访问该 S C C SCC SCC以外的点