Lec 10 深度优先搜索
- DFS
- 全量BFS/DFS
- 图的连通性
- 拓扑排序
- 循环检测
背景: 可到达性问题

策略:设P(s) = None, 然后运行visit(s)
visit(u):
for every v ∊ Adj(u):
if P(v) = None:
Set P(v) = u
Call visit(v)DFS
- 类似于BFS,从s开始搜索图
- 解决单源可达性问题,而不是单源最短路径问题(sP)
- 返回指向s的父指针树
- 思路是: 递归访问出边的邻接顶点,但不重新访问同一个顶点,即沿着任何路径前进直到无法前进,然后回溯直到找到一条未探索的路径继续探索
正确性证明
- 断言: DFS访问顶点v并正确设置P(v),对于从s可达的每个顶点v都成立
- 证明: 对k进行归纳,只对距离s为k的顶点进行断言
- 基本情况(k=0): P(s) 被正确设置为s 并被s访问
- 归纳步骤,考虑顶点v满足
- 考虑顶点u,即从s到v的某条最短路径上的倒数第二个顶点(即v前一个顶点)
- 根据归纳假设,由于
, DFS访问u并正确设置P(u) - 在访问u时,DFS考虑
要么已经在P中,已被访问(即P(u) null),要么v会在访问u时被访问,无论那种情况, v都被DFS访问并正确添加到P中
分析
- 算法最多访问每个顶点一次,并且对于每个顶点
只花费O(1)时间 - 工作量的上界为
- 不同于BFS, DFS并不返回每个顶点的距离,因此DFS的运行时间为O(|E|)
全量 BFS/DFS
- 假设我们想要搜索整个图,而不仅仅是从一个顶点可达的顶点
- 思路是,对任务未访问的顶点重复执行图搜索算法A
- 重复以下步骤,知道所有顶点都被访问
- 选择一个未访问的顶点s,使用A搜索从s可达的所有顶点
- 我们称这个算法为Full-A算法
- 每个顶点访问一次,所以Full-BFS和Full-DFS的运行时间均为O(|V|+|E|)
图的连通性
- 一个无向图是连通的,如果图中每对顶点之间都有一条路径连接。
- 在有向图中,顶点u 可能从顶点 v 可达,但 v 可能从 u 不可达。
- 对于有向图,连通性更加复杂(我们在本课中不会讨论)。
- Connectivity(G):无向图 G 是否连通?
- Connected Components(G):给定无向图 G = (V, E),返回 V的划分为子集
(连通分量),其中每个 在 G 中是连通的,并且不同连通分量的顶点之间没有边。 - 考虑一个解决单源可达性问题的图算法 A。
- 断言:A 可以用来解决连通分量问题。
- 证明:运行 Full-A。对于每次运行 A,将访问到的顶点放入一个连通分量中。
拓扑排序
- 有向无环图(DAG,Directed Acyclic Graph)是一个不包含有向环的有向图
- 图 G = (V, E) 的拓扑排序是对顶点的一个排序,使得每条边
满足 - 拓扑排序不是唯一的。
- 证明一个有向图, 当且仅当它是一个DAG时,才存在拓扑排序。(练习)
- 如何找到拓扑排序?
- 结束顺序是全量 DFS 完成访问每个顶点的顺序
- 断言: 如果G=(V, E)是一个DAG,则结束顺序的逆序是一个拓扑排序
- 证明: 需要证明对每条边而言, u被排到v之前,即访问v完成的时间在访问u之前。分两种情况
- 如果u在v之前被访问
- 在访问u完成之前,会访问v(通过边(u, v)或者其他方式)。
- 因此访问v完成的时间在访问u之前
- 如果v在u之前被访问
- 由于图是无环的,u不能从v过来
- 因此访问v完成的时间在u之前
- 如果u在v之前被访问
循环检测
如果图G=(V, E)是无环的,Full-DFS将找到一个拓扑排序,那如果Full-DFS的逆结束顺序不是拓扑排序,则G必定包含一个环。
- 检查G是否无环: 对于每条边(u, v),检查在逆结束顺序中,v是否在u之前
- 可以通过哈希表或者是直接访问数组在O(|E|)时间内完成
- 要返回这样的一个环,在Full-DFS中维护路径上回到s的前驱集合
- 断言: 如果G包含一个环,Full-DFS将遍历从v到v的前驱的边
- 证明: 考虑G中有一个环
- 不失一般性地,假设
是Full-DFS在环上访问的第一个顶点,对于每个 ,在访问 完成之前,将访问 并完成访问,考虑边 ,如果 尚未被访问,他将访问被现在访问。因此在访问 完成之前,将第一次访问 (根据对 的假设),所以在访问 完成之前,将考虑边 ,其中 是 的前驱
- 不失一般性地,假设