Lecture 5. 图
图的表示
对于图 $G=(V,E)$,其中 $V$ 是点集,$E$ 是边集。
对于无向图和有向图,都可以用若干 邻接表(adjacency list) 或 邻接矩阵(adjacency matrix) 表示。
- 对于 稀疏(sparse) 图($|E| \ll |V|^2$),通常选择邻接表
- 对于 稠密(dense) 图($|E| \approx |V|^2$),或需要快速查询两点之间是否有边,则用邻接矩阵
邻接表
是 $|V|$ 个链表构成的数组。$Adj[u]$ 包含了满足 $(u,v) \in E$ 的所有节点 $v$。
- 若 $G$ 是有向图,那么所有链表的长度总和,就是 $|E|$
- 若 $G$ 是无向图,那么所有链表的长度总和,就是 $2|E|$
邻接表的缺点是无法快速查找图中是否存在一条边 $(u,v)$。
邻接矩阵
邻接矩阵 $A = (a_{ij})$ 是一个 $|V| \times |V|$ 的零一矩阵,行列标号从 $1$ 到 $|V|$:$$a_{i,j}=\begin{cases}1 & (i,j) \in E \\ 0 & (i,j) \not\in E\end{cases}$$,因此其占用的空间是 $\Theta(|V|^2)$ 级别的。
边带权图
对于边带权的图(有一个函数 $w: E \to \mathbb{R}$),只需要在邻接表里面把这个边权额外存储进去,或者把邻接矩阵的零一改成实数。
BFS
Dijkstra 最短路算法和 Prim 最小生成树算法都有用到类似 BFS 的思想。
对于图 $G = (V,E)$,指定起点 $s$,BFS 可以计算出 $s$ 到其他任意节点的距离。BFS 在遍历到距离起点 $k+1$ 的节点之前,一定会先遍历到距离 $k$ 的节点。
不论是有向图还是无向图,在 广度优先树(breadth-first tree) 当中,节点 $u$ 到 $v$ 的简单路径,都对应着原图当中 $u$ 到 $v$ 的最短路径。
伪代码
- $u.color$ 表示节点 $u$ 的状态,黑白灰三种。白表示未处理,灰表示入队,黑表示处理完毕
- $u.d$ 表示节点 $u$ 距离起点的距离
- $u.\pi$ 表示节点 $u$ 在广度优先树当中的前驱节点
效率分析
- 初始化需要 $O(V)$ 时间。
- 每个节点入队出队一次,每次入队出队需要 $O(1)$ 时间,总共需要 $O(V)$ 时间
- 需要遍历邻接表。邻接表长度 $\Theta(E)$,需要 $O(E)$ 时间
- 故总时间复杂度 $O(V+E)$
DFS
伪代码
- $u.color$ 表示节点 $u$ 的状态,黑白灰三种。白表示未处理,灰表示正在处理其子树,黑表示处理完毕
- $u.d$ 表示开始处理的时间戳,是 discovered 的缩写
- $u.f$ 表示处理完毕后的时间戳,是 finished 的缩写
效率分析
- 初始化需要 $O(V)$ 的时间
- 每个节点记录 $u.d$ 和 $u.f$,总共需要 $O(V)$ 时间
- 需要遍历邻接表。邻接表长度 $\Theta(E)$,需要 $O(E)$ 时间
- 故总时间复杂度 $O(V+E)$
括号化定理
在无向图或有向图当中,节点 $u$, $v$ 有以下可能的关系
- 深度优先森林中,$u$ 不是 $v$ 的后代,$v$ 也不是 $u$ 的后代:$[u.d, u.f]$ 与 $[v.d, v.f]$ 完全分离
- 深度优先树中,$u$ 是 $v$ 的后代,$[u.d, u.f] \subset [v.d, v.f]$
- 深度优先树中,$v$ 是 $u$ 的后代,$[v.d, v.f] \subset [u.d, u.f]$
边的分类
一条边 $(u,v)$ 有以下四种分类:
- 树边 tree edges,表示 $v$ 是由 $u$ 直接发现的
- 后向边 back edges,表示深度优先树中 $u$ 的祖先是 $v$。有向图的自环也算后向边
- 前向边 forward edges,表示深度优先树中 $u$ 的后代是 $v$
- 横叉边 cross edges,表示所有其他类型的边