Skip to content

Lec 10 深度优先搜索

  • DFS
  • 全量BFS/DFS
  • 图的连通性
  • 拓扑排序
  • 循环检测

背景: 可到达性问题

截屏2024-07-31 18.54.20

策略:设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满足δ(s,v)=k+1
    • 考虑顶点u,即从s到v的某条最短路径上的倒数第二个顶点(即v前一个顶点)
    • 根据归纳假设,由于δ(s,u)=k, DFS访问u并正确设置P(u)
    • 在访问u时,DFS考虑vAdj(u)要么已经在P中,已被访问(即P(u) null),要么v会在访问u时被访问,无论那种情况, v都被DFS访问并正确添加到P中

分析

  • 算法最多访问每个顶点一次,并且对于每个顶点vAdj(u) 只花费O(1)时间
  • 工作量的上界为uVO(1)×deg(u)=O(E)
  • 不同于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的划分为子集 ViV(连通分量),其中每个 Vi 在 G 中是连通的,并且不同连通分量的顶点之间没有边。
  • 考虑一个解决单源可达性问题的图算法 A。
  • 断言:A 可以用来解决连通分量问题。
  • 证明:运行 Full-A。对于每次运行 A,将访问到的顶点放入一个连通分量中。

拓扑排序

  • 有向无环图(DAG,Directed Acyclic Graph)是一个不包含有向环的有向图
  • 图 G = (V, E) 的拓扑排序是对顶点的一个排序,使得每条边(u,v)E满足 f(u)<f(v)
  • 拓扑排序不是唯一的。
  • 证明一个有向图, 当且仅当它是一个DAG时,才存在拓扑排序。(练习)
  • 如何找到拓扑排序?
    • 结束顺序是全量 DFS 完成访问每个顶点的顺序
    • 断言: 如果G=(V, E)是一个DAG,则结束顺序的逆序是一个拓扑排序
    • 证明: 需要证明对每条边而言, u被排到v之前,即访问v完成的时间在访问u之前。分两种情况
      • 如果u在v之前被访问
        • 在访问u完成之前,会访问v(通过边(u, v)或者其他方式)。
        • 因此访问v完成的时间在访问u之前
      • 如果v在u之前被访问
        • 由于图是无环的,u不能从v过来
        • 因此访问v完成的时间在u之前

循环检测

如果图G=(V, E)是无环的,Full-DFS将找到一个拓扑排序,那如果Full-DFS的逆结束顺序不是拓扑排序,则G必定包含一个环。

  • 检查G是否无环: 对于每条边(u, v),检查在逆结束顺序中,v是否在u之前
  • 可以通过哈希表或者是直接访问数组在O(|E|)时间内完成
  • 要返回这样的一个环,在Full-DFS中维护路径上回到s的前驱集合
  • 断言: 如果G包含一个环,Full-DFS将遍历从v到v的前驱的边
  • 证明: 考虑G中有一个环(v0,v1,...vk,v0)
    • 不失一般性地,假设v0是Full-DFS在环上访问的第一个顶点,对于每个vi,在访问vi完成之前,将访问vi+1并完成访问,考虑边(vi,vi+1),如果vi+1尚未被访问,他将访问被现在访问。因此在访问v0完成之前,将第一次访问vk(根据对v0的假设),所以在访问vk完成之前,将考虑边(vk,v0),其中v0vk的前驱