文章目录
版权声明
- Algorithms 系列学习笔记来源于 Kevin Wayne 和 Robert Sedgewick 在 Coursera 网站上所授课程 Algorithms, parts I and II [1,2],课程教材 Algorithms 4th edition [3];
- 该系列笔记不以盈利为目的,仅用于个人学习、课后复习及科学研究;
- 如有侵权,请与本人联系([email protected]),经核实后即刻删除;
- 本文采用 署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0) 协议发布;
4. 图
4.1 Background
- 常见的图模型:
- 无向图;
- 有向图;
- 加权图;
- 加权有向图;
- 词汇:
- adjacency, n. 邻接;
- adjacency list,邻接表;
- vertex,n. 顶点;
- graph, n. 图表,曲线图;
- digraph, n. 有向图;
- 相似的词汇:diagram, n. 图表,图解;
- adjacency, n. 邻接;
4.2 无向图
4.2.1 无向图相关的定义
-
度数:依附于某个顶点的边的数量;
-
平行边和自环:
-
平行边:
-
无向图中的平行边:连接一对顶点的无向边多于1条;
-
有向图中的平行边:连接一对顶点且起点和终点相同的有向边多于1条;
-
-
-
自环:某条边连接的两个顶点为同一点;
- 没有自环的顶点,仍可到达自身,E.g. 若顶点 V 无自环,仍认为 V 点可到达自身;
-
简单图与多重图:
- 简单图:不含平行边和自环的图;
- 多重图:含有平行边的图;
-
简单环和简单路径:
- 简单路径:一条无重复顶点的路径;
- 简单环:除起点和终点外,不含有重复顶点和边的环;
-
连通图、极大连通子图和非连通图:
- 连通图:从图中任一顶点均存在一条路径到达另一顶点;
- 极大连通子图;
- 非连通图:其中包含多个连通分量(或称连通子图);
-
树、森林、生成树和生成树森林:
- 树:一幅无环连通图;
- 森林:一组互不相连的树组成的集合;
- 生成树:连通图的生成树是该图的一幅子图,含有图中所有的顶点,且是一棵树;
- 生成树森林:所有连通子图的生成树的集合;
- 仅在非连通图中,才有多个连通子图;
-
图的密度、稀疏图、稠密图和二分图:
- 图的密度:已连接的顶点对占所有可能连接的顶点对的比例;
- 稀疏图:图的密度低;
- 稠密图;
- 二分图:二分图的所有顶点可被分成两个互斥的集合,使得所有边连接的一对顶点分别属于两个集合;
- 等价定义:二分图中任一环的顶点数量为偶数;
-
无向图中常见问题的解决方案:
问题 | 解决方案 |
---|---|
单点连通性 | DFS |
单点路径 | DFS |
单点最短路径 | BFS |
连通性 | Union-find / DFS |
检测环 | DFS |
检测二分图 | DFS |
4.2.2 图的表示方法
- 图的表示方法应满足的条件:
- 空间开销小;
- 对各种实例方法的实现效率高;
- 能表示平行边;
- 常见的图表示方法:
- 邻接矩阵:
- 空间开销大: ;
- 不能表示平行边;
- 边的数组:
Edge
类中含有2个int
实例变量表示一条边;- 对部分实例方法的实现效率低;
- E.g. 返回与给定点相邻的所有点的集合;
- 邻接表:即邻接表数组,以顶点为索引的
bag
数组;- 该方法满足图表示方法中的要求;
- 邻接集:相较于邻接表,具有较高的时间、空间复杂度;
- 使用符号表代替由顶点索引构成的数组;
- 便于增删顶点;
- 使用
set
替代bag
;- 便于删除某条边;
- 便于检查判断某条边的存在性;
- 使用符号表代替由顶点索引构成的数组;
- 符号图:用字符串表示顶点;
- 使用符号表将字符串映射为数值索引;
- 使用
String
数组实现反向索引; - 使用邻接表表示无向图;
- 邻接矩阵:
4.2.3 DFS & BFS
- 深度优先搜索:
- 用到的数据结构:栈;
- 求解两点是否连通;
- 广度优先搜索:
- 用到的数据结构:队列;
- 求解两点是否连通及最短路径;
4.2.4 连通分量
-
Union-find 算法与DFS的对比:
-
DFS:
- 优点:常数时间内实现图的连通性查询;
- 缺点:构建图的过程需要一定的时间开销;
-
Union-find:
- 优点:连通性查询的时间开销接近于常数;
-
注意:
- 若仅需完成连通性查询和插入操作,则使用 Union-find 算法;
- 若处理已有图数据,则使用 DFS;
-
-
检测图中是否有环:若搜索到此前已访问过的节点,则表明有环;
-
判断一幅图是否为二分图:
- 构建一个
boolean
数组,给每个顶点染色; - 若搜索到此前已访问过的节点,则比较当前顶点的颜色与已访问顶点的颜色,当两者相同时,该图不为二分图;
- 构建一个
4.3 有向图
4.3.1 有向图的相关定义
-
入度与出度:
- 入度:指向该顶点的边的总数;
- 出度:由该顶点指出的边的总数;
-
简单有向环和有向环:
- 简单有向环:除起点和终点外,不含有重复的顶点和边;
-
可达性:约定每个顶点均能到达自身;
- 由于每条边都有方向,有向图中的可达性不同于无向图中的连通性;
-
有向无环图:DAG,Directed Acylic Graph;
- acylic,adj. 非循环的,非周期的;
-
拓扑排序:
- 给定一幅有向图,将所有顶点排序,使得所有的有向边均从排序靠前的元素指向排序靠后的元素;
- 如若无法实现该过程,则应予以说明;
-
强连通:两个顶点相互可达;
-
自反性:任意顶点与其自身为强连通关系;
-
两顶点间为强连通关系:当且仅当两个顶点处于同一个有向环中;
-
强连通性:一幅有向图中任意两个顶点为强连通关系;
-
-
强连通分量:互为强连通的顶点组成的最大子集;
4.3.2 环与有向无环图
- 优先级限制下的调度问题:
- 该问题等价于计算有向无环图中所有顶点的拓扑排序;
- 若存在有向环,则该问题无解;
- 使用 DFS 遍历图中各顶点的顺序:
- pre:前序,在递归调用之前将顶点加入队列;
- post:后序,在递归调用之后将顶点加入队列;
- reversePost:逆后序,递归调用之后将顶点加入栈;
- 有向无环图的逆后序排列即为拓扑排序结果;
- 为什么需要新建一个
boolean
数组表示递归调用时栈上的所有顶点,而不是借助于表示已被访问顶点的数组?- E.g. 在 [3] Page 372,寻找有向环的源程序:
- 布尔型数组
marked
表示已被访问的顶点,表示当前已访问过的所有顶点; - 布尔型数组
onStack
表示对某个顶点执行 DFS 时栈上的所有点,即单条路径上的所有点,便于检测有向环;
- 布尔型数组
- E.g. 在 [3] Page 372,寻找有向环的源程序:
4.3.3 强连通性
- 有向图 G 的强连通分量与反向图 的强连通分量相同;
- Kosaraju 算法:
- 对于一幅给定的有向图 G,使用 DFS 求解其反向图 的逆后序排列;
- 按照上一步骤得到的逆后序排列,使用 DFS 遍历各个顶点;
- 在同一次 DFS 递归调用中被访问到的节点均属于同一个强连通分量;
4.4 加权图
4.4.1 加权图的相关定义
- 权重:可为0或负值;
- 生成树:一幅图的生成树,指的是含有图中所有顶点的无环连通子图;
- 最小生成树:MST,minimum spanning tree,加权图的最小生成树是一棵各条边权值之和最小的生成树;
- 若图中各条边的权重均不相同,则最小生成树可唯一确定;
- 最小生成树森林:当图中存在多个连通分量时,对每个连通分量求得的最小生成树的集合;
- 树的两个性质:
- 用一条边连接树中的任意两个顶点,将产生一个环;
- 从树中删除任一条边,将得到两棵树;
- 切分定理:对一幅加权图做任意形式的切分,权重最小的横切边必属于最小生成树;
- 切分:将顶点集划分为两个非空且互不重叠的子集;
- 横切边:连接切分后两个子集的边;
- 切分时有可能产生多条属于最小生成树的横切边,它们分别用于连接不同的顶点;
- 连接相同顶点的属于最小生成树的横切边有且仅有一条;
4.4.2 Prim 算法 & Kruskal 算法
- 最小生成树算法:
- Prim 算法;
- Kruskal 算法;
- Prim 算法:每次添加一条边,该边连接当前树中的顶点与不在树中的顶点,且为权重最小的横切边;
- 延时实现:优先队列中含有已失效的边(即边的两个节点均已被纳入最小生成树中);
- 即时实现:存储不在树中的顶点与树中顶点相连的权重最小的边;
- Kruskal 算法:按照边的权重由大到小排列,使用权重最小的边连接各顶点,从而得到最小生成树;
4.5 加权有向图
4.5.1 加权有向图的相关定义
- SPT:Shortest Path Tree,最短路径树;
- 包含起点至任意可达顶点的最短路径;
- 实现最短路径的数据结构:
- 数组
edgeTo[]
:存储最短路径树中连接该顶点与父节点的边; - 数组
distTo[]
:从起点至该顶点的最短路径长度; - 约定:对起点 s 有
edgeTo[s]=null, distTo[S]=0
,从起点至不可达顶点的距离为无穷大;
- 数组
- 松弛操作:relaxation;
- 已知从起点 s 顶点 v 、w 的最短路径长度分别为 ,边 的权重为 ;
- 若 ,则更新 ;
- 负权重环:环上各边的总权重为负;
- 含有负权重环时,不存在最短路径;
4.5.2 适用于有向加权图的算法
-
Dijkstra 算法:
-
算法描述:对离起点最近的非树顶点依次执行松弛操作;
-
适用范围:非负权值的有向图,无论图中是否有环均可处理;
-
注意:将无向图视为强连通图,即可将无向图视为有向图处理;
-
-
无环加权有向图中的最短路径算法:
- 算法描述:按照拓扑排序,依次放松各顶点;
- 适用范围:无环加权有向图,可处理负权值的边;
- 时间复杂度: ,线性时间复杂度下,求解最短路径树(单点最短路径问题);
- 注意:处理无环加权有向图时,该方法相较于 Dijkstra 算法速度更快;
-
无环加权有向图中的最长路径算法:
- 算法描述:复制原始无环加权有向图,得到一个副本,将副本中的所有边的权重取相反数,在副本中求得的最短路径即为原图中的最长路径;
-
关键路径方法:解决优先级限制下的并行任务调度问题;
- 算法描述:将优先级限制下的并行任务调度问题转换为无环加权有向图中的最长路径问题;
- 创建一幅无环加权有向图,包含一个起点和一个终点;
- 为每个任务创建一条边 ,权重为该项任务的耗时;
- 添加一条边,从一个任务的终点指向另一个任务的起点,权重为0,用于表示任务之间的优先级限制;
- 添加一条边从起点指向 ,权重为0;
- 添加一条边从终点指向 ,权重为0;
- 注意:优先级限制下的任务调度中若存在环路,则该问题无解;
- 关键路径:即最长路径;
- 时间复杂度:线性时间复杂度;
- 算法描述:将优先级限制下的并行任务调度问题转换为无环加权有向图中的最长路径问题;
-
加权有向图中的最短路径算法:相对最后期限限制下的并行任务调度问题(可能存在环和负权值):
- 算法描述:将相对最后期限限制下的并行任务调度问题转换为加权有向图中的最短路径问题;
- 若两个任务间存在最后期限限制,则添加一条边从某个任务的起点指向另一个任务的终点,权重为期限限制值的相反数;
- 将所有边的权重取反,使问题转换为求解最短路径;
- 注意:相对最后期限限制下的任务调度中可能存在环路,且该问题有解;
- 算法描述:将相对最后期限限制下的并行任务调度问题转换为加权有向图中的最短路径问题;
4.5.3 一般加权有向图中的最短路径问题
- 一般加权有向图:可能含有环和负权重边;
- 最短路径的存在性:若起点至某一顶点路径上的所有节点,均不属于任意负权重环时,则存在从起点至该顶点的最短路径;
- Bellman-ford 算法:
- 算法描述:将被成功放松的边所指向的顶点加入队列,并周期性地检查
edge[]
表示的子图中是否存在负权重环; - 适用范围:求解一般加权有向图中的单点最短路径;
- 检测是否含有负权重环,避免陷入死循环;
- 时间复杂度: ;
- 空间复杂度: ;
- 算法描述:将被成功放松的边所指向的顶点加入队列,并周期性地检查
4.6 图搜索算法总结
-
表示时间开销的符号含义:
- V:vertex,图中顶点数量;
- E:edge,图中边的数量;
-
各项任务的时间开销:
-
在有向图中,DFS 标记由一个集合的顶点可达的所有顶点所需的时间与被标记的所有顶点的出度之和成正比;
-
使用 DFS 对有向无环图进行拓扑排序:
- 时间开销:2(V+E);
- 分析:2次 DFS;
- 第一次 DFS:检测是否存在有向环;
- 第二次 DFS:获取顶点的逆后序排列,作为拓扑排序结果;
-
有向图部分各项任务的时间开销:
- Kosaraju 算法:
-
时间开销:与 V+E 成正比;
-
分析:将有向图反向,并执行2次 DFS;
- 将有向图反向:与 V+E 成正比;
- 第一次 DFS:检测是否存在有向环;
- 第二次 DFS:获取顶点的逆后序排列,作为拓扑排序结果;
-
- Kosaraju 算法:
-
加权图部分各项任务的时间开销:
- Prim 算法的延时实现:
- 时间复杂度: ;
- 空间复杂度: ;
- Prim 算法的即时实现:
- 时间复杂度: ;
- 空间复杂度: ;
- Kruskal 算法:
- 时间复杂度: ;
- 空间复杂度: ;
- 注意:Kruskal 算法稍慢于 Prim 算法;
- Prim 算法的延时实现:
-
有向加权图部分各项任务的时间开销:
- Dijkstra 算法:
- 时间复杂度:
- 一般情况: ;
- 最坏情况: ;
- 空间复杂度: ;
- 适用范围:边的权重必须为正;
- 优点:最坏情况下仍有较好性能;
- 时间复杂度:
- 基于拓扑排序的最短路径算法:
- 时间复杂度:
- 一般情况: ;
- 最坏情况: ;
- 空间复杂度: ;
- 适用范围:仅适用于无环加权有向图;
- 优点:无环图中的最优算法;
- 时间复杂度:
- Bellman-Ford 算法:基于最小优先队列;
- 时间复杂度:
- 一般情况: ;
- 最坏情况: ;
- 空间复杂度: ;
- 适用范围:图中无负权重环;
- 优点:适用领域广泛;
- 时间复杂度:
- Dijkstra 算法:
-
-
各种图的表示方法:
图的类型 表示方法 备注 无向图 邻接表 链表中的节点为某条边的另一个顶点 有向图 邻接表 链表中的节点为某条边的另一个顶点 加权图 邻接表 链表中的节点为某条边的两个顶点和边的权重 有向加权图 邻接表 链表中的节点为某条边的两个顶点和边的权重
References
[1] https://www.coursera.org/learn/algorithms-part1?.
[2] https://www.coursera.org/learn/algorithms-part2?.
[3] Sedgewick, R. & Wayne, K. (2016). Algorithms Fourth Edition. Boston: Addison-Wesley.