相关笔记

概览

本节介绍 强连通分量(Strongly Connected Components, SCC) 的概念及 Kosaraju 算法,该算法利用两次 20.3 深度优先搜索 时间内找出有向图的所有强连通分量。核心知识点包括:

  • 强连通分量的定义:有向图中顶点间互相可达的最大子集
  • Kosaraju 算法三步走:①对 做 DFS 计算完成时间 ②计算转置图 ③按完成时间递减对 做 DFS
  • 分量图(condensation):将每个 SCC 缩为一个超顶点,得到的分量图是 20.4 拓扑排序 中的 DAG
  • 定理20.9:Kosaraju 算法的正确性
  • 引理20.10~20.14:构成正确性证明链的五个关键引理

知识结构总览

flowchart TD
    A["20.5 强连通分量"] --> B["基本概念"]
    A --> C["Kosaraju 算法"]
    A --> D["正确性证明链"]
    A --> E["分量图"]
    A --> F["应用与拓展"]

    B --> B1["强连通: u 可达 v 且 v 可达 u"]
    B --> B2["SCC: 最大强连通子集"]
    B --> B3["SCC 划分: 每个顶点恰属于一个 SCC"]

    C --> C1["第一步: DFS(G) 计算完成时间"]
    C --> C2["第二步: 计算 G^T"]
    C --> C3["第三步: 按完成时间递减 DFS(G^T)"]

    D --> D1["引理20.10: SCC 内顶点的完成时间区间"]
    D --> D2["引理20.11: 分量图的边与完成时间"]
    D --> D3["引理20.12: G^T 中的 SCC 树"]
    D --> D4["引理20.13: G^T 中根的 SCC"]
    D --> D5["引理20.14: G^T 中 DFS 树的根"]

    E --> E1["分量图 G^SCC 是 DAG"]
    E --> E2["分量图的拓扑排序"]

    F --> F1["Kosaraju vs Tarjan SCC"]
    F --> F2["PageRank 预处理"]
    F --> F3["编译器循环检测"]
    F --> F4["2-SAT 求解"]

核心概念

强连通与强连通分量

强连通(Strongly Connected)

在有向图 中,如果两个顶点 满足 可达 (存在从 的有向路径)且 可达 ,则称 强连通的。

性质: 强连通关系是 上的一个等价关系(自反性、对称性、传递性均成立)。

强连通分量(Strongly Connected Component, SCC)

有向图 的一个强连通分量是一个最大顶点集合 ,使得 中任意两个顶点互相可达。

“最大”的含义: 不存在更大的顶点集合 使得 中所有顶点互相可达。SCC 是强连通等价关系的等价类。

强连通分量的直观理解

SCC 就是图中”紧密抱团”的一组顶点——组内任意两个顶点可以互相到达,但组外的顶点无法同时到达组内所有顶点并被组内所有顶点到达。

类比: 想象一个社交网络中的”朋友圈”。如果 A 能联系到 B,B 能联系到 C,C 也能联系到 A,那么 A、B、C 就形成一个”强连通分量”——他们之间可以互相传达消息。但如果 D 只能被 A 联系到,D 却联系不到 A,那么 D 不在这个分量中。

转置图

转置图(Transpose Graph)

给定有向图 ,其转置图 ,其中

直观理解: 就是将 中所有边的方向反转。 可以在 时间内从 的邻接表表示中构造出来。

转置图的重要性质

具有完全相同的 SCC。这是因为强连通关系是对称的:如果 可达 (在 中),那么在 可达 。因此 中强连通当且仅当它们在 中强连通。

Kosaraju 算法

Kosaraju 算法(1978)通过两次 DFS 在 时间内找出所有 SCC。

算法执行流程

  1. 对原图 G 调用 DFS,记录每个顶点的完成时间 f[v]
  2. 计算 G 的转置图 G^T(将所有边方向反转)
  3. 按完成时间递减的顺序,对 G^T 调用 DFS
  4. 第二次 DFS 中每棵 DFS 树就是一个强连通分量
flowchart TD
    A["开始:输入有向图 G"] --> B["第一次 DFS(G)<br/>记录每个顶点的完成时间 f[v]"]
    B --> C["计算转置图 G^T<br/>反转所有边的方向"]
    C --> D["按 f[v] 递减顺序排列顶点"]
    D --> E["取下一个顶点 u"]
    E --> F{"u 已被访问?"}
    F -- 是 --> G{"还有未处理的顶点?"}
    F -- 否 --> H["从 u 开始对 G^T 调用 DFS-VISIT"]
    H --> I["本次 DFS-VISIT 访问的所有顶点<br/>构成一个强连通分量"]
    I --> G
    G -- 是 --> E
    G -- 否 --> J["结束:输出所有强连通分量"]
STRONGLY-CONNECTED-COMPONENTS(G)
1  调用 DFS(G) 计算每个顶点的完成时间 f[v]
2  计算 G 的转置图 G^T
3  按照完成时间 f[v] 递减的顺序,对 G^T 调用 DFS
4  第三步中产生的每一棵 DFS 树就是一个强连通分量

Kosaraju 算法的核心直觉

为什么需要转置图?为什么按完成时间递减?

核心思想分为两步理解:

  1. 第一次 DFS(在 上):完成时间最晚的顶点一定位于某个”源 SCC”(source SCC)中——即分量图中没有入边的 SCC。这是因为源 SCC 中的顶点不会被其他 SCC 中的顶点”阻挡”,DFS 会深入到源 SCC 的最深处。

  2. 第二次 DFS(在 上):转置图 将源 SCC 变成了”汇 SCC”(sink SCC)。按完成时间递减处理,就先处理 的汇 SCC(即 的源 SCC),从中探索能到达的所有顶点——它们恰好构成一个完整的 SCC(因为在 中,源 SCC 变成了汇,不会被其他 SCC 的顶点”泄漏”到)。

类比: 想象水流从高处(源 SCC)流向低处。第一次 DFS 找到”最高处”的水源,第二次 DFS 在反转的图(水从低处流回高处)中从这些水源开始收集所有能流回的顶点——它们恰好形成一个 SCC。

分量图(Component Graph / Condensation)

分量图

给定有向图 ,其分量图 定义为:

  • 每个顶点 对应 的一个强连通分量
  • 当且仅当 中存在从 中某顶点到 中某顶点的边

分量图也称为 缩图(condensation)。

引理(分量图是 DAG)

有向图 的分量图 是一个有向无环图(DAG)。

分量图的意义

分量图将任意有向图”简化”为 DAG。在分量图上可以应用 20.4 拓扑排序 的所有算法——例如对分量图做拓扑排序,就得到了 SCC 之间的”依赖顺序”。这是 Kosaraju 算法正确性的基础。


正确性证明链

Kosaraju 算法的正确性由五个引理(引理20.10~20.14)和一个定理(定理20.9)构成完整的证明链。下面逐一展开。

引理20.10(SCC 内顶点的完成时间区间)

引理20.10

是有向图 的两个不同的强连通分量。如果 中存在一条边 ,其中 ,则

这里 表示分量 中所有顶点完成时间的最大值。

引理20.10 的直观含义

如果 SCC 有一条边指向 SCC ,则 的”完成时间”晚于 。这意味着在分量图的拓扑排序中, 排在 前面——分量图中从 有边,对应拓扑排序中 在前。

引理20.11(分量图中边的方向与完成时间)

引理20.11

是有向图 的两个不同的强连通分量,且分量图 中存在一条从 的边。则

引理20.12( 中的 SCC 树)

引理20.12

是有向图 的一个强连通分量, 中在 的第二次 DFS 中第一个被发现的顶点。则在 的 DFS 中,以 为根的 DFS 树恰好包含 中的所有顶点(不多不少)。

引理20.13( 中根的 SCC)

引理20.13

的第二次 DFS 中,每棵 DFS 树的根 对应的 SCC 满足:在 的分量图 中, 没有入边(即 中的源 SCC)。

引理20.14( 中 DFS 树的根对应源 SCC)

引理20.14

的第二次 DFS 中, 的分量图 中的源 SCC 对应的顶点会成为 DFS 树的根。

定理20.9(Kosaraju 算法的正确性)

定理20.9

STRONGLY-CONNECTED-COMPONENTS 过程正确地计算出有向图 的所有强连通分量。

证明链的逻辑结构

五个引理构成一条严密的逻辑链:

  • 引理20.10:建立 SCC 间完成时间的基本不等式(核心工具)
  • 引理20.11:将引理20.10 推广到分量图的边
  • 引理20.12:证明 的 DFS 树恰好对应一个 SCC(关键步骤)
  • 引理20.13:证明 DFS 树的根对应源 SCC
  • 引理20.14:证明源 SCC 一定会成为 DFS 树的根
  • 定理20.9:综合以上引理得出算法正确性

补充理解与拓展

Kosaraju 算法 vs Tarjan SCC 算法

补充:两种经典 SCC 算法的全面对比

来源: S. R. Kosaraju, “Analysis of a Simple Linear Time Path Finding Algorithm”, 1978; R. E. Tarjan, “Depth-First Search and Linear Graph Algorithms”, SIAM Journal on Computing, 1(2), 1972

比较维度Kosaraju 算法 (1978)Tarjan SCC 算法 (1972)
核心思想两次 DFS + 转置图一次 DFS + 栈
DFS 次数2 次1 次
是否需要转置图需要(第二步构造 不需要
额外空间(转置图)(栈 + 数组)
时间复杂度
实现难度简单直观较复杂(需要维护栈和 lowlink)
历史地位教学经典历史更早,影响深远
实际使用竞赛编程(实现简单)需要单次遍历时

Tarjan 算法的核心思想: 在一次 DFS 中维护一个栈,记录当前正在探索的顶点。当发现某个顶点的”lowlink”值等于自身的发现时间时,弹出栈中从该顶点开始的所有顶点——它们构成一个 SCC。lowlink 值表示从当前顶点出发,通过树边和最多一条后向边/交叉边能到达的发现时间最小的顶点。

选择建议: 竞赛编程中 Kosaraju 更受欢迎(代码短、不易出错);需要最小化空间或只需要一次图遍历时,Tarjan 更优。

SCC 的实际应用

补充:强连通分量的工程应用

来源: 综合整理

  1. PageRank 预处理:Google 的 PageRank 算法在计算网页排名前,先将网页图分解为 SCC。每个 SCC 内的网页具有相同的 PageRank 值(因为互相可达),只需对分量图(DAG)计算 PageRank,大幅减少计算量。

  2. 编译器循环检测:编译器的控制流分析中,基本块之间的跳转关系构成有向图。检测 SCC 可以识别循环结构(loop),这是编译器优化的基础——循环不变量外提、强度削弱等优化都依赖循环的识别。

  3. 2-SAT 求解:2-SAT 问题(每个子句恰好包含两个文字的布尔可满足性问题)可以通过构造”蕴含图”(implication graph),求 SCC 来判定可满足性。如果某个变量 属于同一个 SCC,则 2-SAT 不可满足;否则可满足。

  4. 社交网络社区发现:在 Twitter/X 等社交平台中,用户之间的关注关系(有向)可以分解为 SCC。大型 SCC 代表”紧密社区”——社区内信息可以传播到每个成员。

分量图的性质和应用

补充:分量图(Condensation)的深层性质

来源: 教材第20.5节

分量图 是 DAG,因此可以应用 DAG 上的所有算法:

  • 拓扑排序:对 做拓扑排序,得到 SCC 之间的依赖顺序
  • 最长路径 上的最长路径对应原图中”最长 SCC 链”
  • 传递闭包 的传递闭包可以通过对 计算传递闭包,再扩展到 SCC 内部得到

分量图的顶点数 的顶点数等于 的 SCC 数量。对于”稀疏 SCC”的图(大多数 SCC 很小), 远小于 ,在其上操作更高效。

半连通性判定(习题22.5-7)

补充:半连通性

来源: 习题22.5-7

有向图 半连通的(semiconnected),如果对任意两个顶点 ,要么 可达 ,要么 可达

判定算法:

  1. 计算 的所有 SCC
  2. 构造分量图
  3. 做拓扑排序
  4. 检查拓扑排序中相邻 SCC 之间是否都有边(即 的拓扑排序中,每对相邻顶点之间都有边)
  5. 如果是,则 半连通;否则不半连通

时间复杂度:

正确性: 半连通当且仅当 的拓扑排序中每对相邻顶点之间有边。如果存在相邻 SCC 之间没有边,则 中顶点不可达 中顶点(因为拓扑排序中 排在 前面, 排在 后面的所有 SCC 前面,如果没有从 的边,则 中顶点无法到达 中顶点),且 中顶点也不可达 中顶点(因为 排在 后面),违反半连通性。


易混淆点与辨析

误区:无向图的连通分量和有向图的强连通分量是同一概念

错误理解: “强连通分量就是无向图的连通分量在有向图中的推广。”

正确理解: 无向图的连通分量只要求顶点之间有路径(无向路径),而有向图的强连通分量要求双向可达。有向图还有”弱连通分量”的概念——忽略边的方向后得到的连通分量。

关系: 每个 SCC 是弱连通分量的一个子集。一个弱连通分量可以包含多个 SCC。

误区:单个顶点不构成 SCC

错误理解: “SCC 至少要有两个顶点。”

正确理解: 单个顶点本身就是一个 SCC(平凡 SCC)。如果一个顶点不与任何其他顶点强连通,它自己就构成一个 SCC。在 Kosaraju 算法中,每个顶点恰好属于一个 SCC——要么是平凡 SCC,要么是非平凡 SCC。

误区:Kosaraju 算法中第二次 DFS 的顺序不重要

错误理解: “第二次 DFS 可以按任意顺序处理顶点。”

正确理解: 第二次 DFS 必须按第一次 DFS 的完成时间递减顺序处理顶点。这是算法正确性的关键——只有按这个顺序,才能保证先处理 中的源 SCC(在 中是汇 SCC),从而正确地”剥离”每个 SCC。

反例: 如果按完成时间递增处理,可能先处理非源 SCC,导致 DFS 树跨越多个 SCC,无法正确分离。

误区:转置图改变了 SCC 的结构

错误理解: “转置图 的 SCC 和 的 SCC 不同。”

正确理解: 具有完全相同的 SCC。强连通关系是对称的( 可达 可达 ),反转所有边的方向不改变这种双向可达性。转置图改变的只是 SCC 之间的边方向。


习题精选

题号题目描述难度
22.5-1图 20-10 中有向图的 SCC 有哪些?
22.5-2给出一个 时间算法,判断有向图 是否只有一个 SCC
22.5-3给出一个 时间算法,将有向图 的每个 SCC 缩为单个顶点,构造分量图⭐⭐
22.5-4证明:如果 的分量图中有从 SCC 到 SCC 的路径,则 ⭐⭐
22.5-5给出一个 时间算法,计算有向图 的分量图中每个 SCC 的入度和出度⭐⭐
22.5-6给出一个 时间算法,将有向图 的顶点划分为”树边 SCC”、“前向边 SCC”、“交叉边 SCC”和”后向边 SCC”⭐⭐⭐
22.5-7给出一个 时间算法,判断有向图 是否是半连通的⭐⭐⭐

由引理20.11,对每条边 ,有

因此 ,即

解题思路提示

  • 22.5-2:判断”只有一个 SCC”等价于”任选一个顶点能到达所有其他顶点且被所有其他顶点到达”。利用 各做一次 DFS/BFS 即可。
  • 22.5-3:先计算 SCC(Kosaraju),再遍历所有边,将端点属于不同 SCC 的边映射到分量图上。
  • 22.5-7:半连通性是”弱化的强连通”——不要求双向可达,只要求单向可达。利用分量图的拓扑排序,将问题简化为检查相邻 SCC 之间是否有边。

视频学习指南

资源主题链接说明
MIT 6.006 Lecture 13Graph Search, Strongly Connected Componentshttps://www.youtube.com/watch?v=za-BT3C4NN8Kosaraju 算法完整讲解
WilliamFisetKosaraju’s Algorithmhttps://www.youtube.com/watch?v=RpgcYiky7QM含逐步演示和代码
Abdul BariStrongly Connected Componentshttps://www.youtube.com/watch?v=Rs6DXyWpWrI直观的 SCC 概念讲解
Tarjan’s SCC by WilliamFisetTarjan’s Algorithmhttps://www.youtube.com/watch?v=wUgWX0nc4OMTarjan 算法对比

教材原文

CLRS 第4版 20.5节原文

In this section, we show how to use depth-first search to decompose a directed graph into its strongly connected components. This section assumes some familiarity with the concept of a strongly connected component, which was defined in Section B.4.

The algorithm we present uses two depth-first searches. The first depth-first search is performed on the original graph , and the second is performed on the transpose of . The transpose of a directed graph is the graph , where . That is, is with all its edges reversed. Given an adjacency-list representation of , the time to create is .

STRONGLY-CONNECTED-COMPONENTS() 1 call DFS() to compute finishing times for each vertex 2 compute 3 call DFS(), but in the main loop of DFS, consider the vertices in order of decreasing (as computed in line 1) 4 output the vertices of each tree in the depth-first forest formed in line 3 as a separate strongly connected component

Theorem 20.9: The STRONGLY-CONNECTED-COMPONENTS procedure correctly computes the strongly connected components of the directed graph .

The intuition behind why the algorithm works is that the first depth-first search identifies the “source” strongly connected components, and the second depth-first search, on the transpose, peels off the strongly connected components one by one.


参见Wiki: 第20章_基本图算法-章节汇总 | 20.3 深度优先搜索 | 20.4 拓扑排序

第20章-基本图算法 #学习/算法导论/基本图算法/强连通分量