图论
0 前言
1 图的存储
1.1 邻接矩阵
代码实现
1 | int matrix[n][m]; |
优点
- 容易判断两个顶点间是否有边
- 容易计算顶点的度
1.2 邻接表
1 | // 定义一个vector数组 |
1.3 对比
degree是度的意思。
| 存储方案 | 空间复杂度 | 判断 i→j 是否有边 | 遍历所有邻居 | 适用场景 |
|---|---|---|---|---|
| 邻接矩阵 | \(O(n^2)\) | \(O(1)\) | \(O(n)\) | 顶点数较少、稠密图 |
| 邻接表 | \(O(n + e)\) | \(O(degree(i))\) | \(O(degree(i))\) | 顶点数较多、稀疏图 |
2 图的遍历
2.1 深度优先搜索(DFS)
深度优先搜索 (Depth First Search, DFS) 是一种用于遍历或搜索树或图的算法。它的核心思想是“不撞南墙不回头”:从起始节点开始,沿着一条路径尽可能深地探索下去,直到无法继续(到达叶子节点或已访问节点),然后回溯到上一个节点,继续尝试探索其他分支。
DFS 遵循“深度优先”策略。在访问一个起始顶点 \(v\) 后,它会依次从 \(v\) 未被访问的邻接点出发,进行深度优先遍历。
- 数据结构:DFS 本质上是利用了栈
(Stack) 的后进先出 (LIFO)
特性。在代码实现中,通常使用递归(隐式使用系统栈)或显式定义的
std::stack - 状态标记:为了防止在图中陷入死循环(环路),必须维护一个
visited数组来记录哪些节点已经被访问过
2.2 广度优先搜索(BFS)
广度优先搜索 (Breadth First Search, BFS) 是一种按“层”推进的遍历算法。它从起始节点开始,先访问所有距离为 1 的邻居,再访问所有距离为 2 的邻居,以此类推。
BFS 遵循“横向扩展”策略。它会先均匀地向四周探索,只有当这一层的所有节点都被访问过后,才会进入下一层。
- 数据结构:BFS 核心是利用了队列 (Queue) 的先进先出 (FIFO) 特性
- 状态标记:与 DFS 一样,必须使用
visited数组记录已访问节点,防止死循环 - 最短路径性质:在无权图中,BFS 第一次到达某个节点时,所经过的路径长度一定是该节点到起点的最短路径
3 回路
3.1 欧拉回路
从一个点出发,刚好经过每条边一次,然后回到原点,每个点要有进有出,即每个点的度必须是偶数。
eg:一笔画问题。
3.2 汉密尔顿回路
从一个点出发,沿着边走,刚好经过每个顶点一次,之后回到出发点。
4 拓扑排序
有向无环图里所有顶点的线性序列,其中:
- 所有顶点出现且只出现一次
- 若存在一条A->B的路径,序列中A一定在B前面
如何找拓扑排序:
- 从图里选择一个没有前驱(入度为0)的顶点
- 从该图中删除该顶点和以它为起点的有向边
每个有向无环图有一个或多个拓扑排序。
5 最小生成树
最小生成树使图联通并且所有边的权值之和最小。
5.1 Prim算法
普利姆算法。
- 从任意节点出发来寻找最小生成树
- 某个点加入到被选取的点中后,解锁这个点出发的所有新的边
- 在所有解锁的边中选最小的边,然后看看这个边会不会形成环
- 如果会,不要当前边,继续考察剩下解锁的边中最小的边,重复3)
- 如果不会,要当前边,将该边的指向点加入到被选取的点中,重复2
- 当所有点都被选取,最小生成树就得到了
5.2 Kruskal算法
克鲁斯卡尔算法。
- 总是从权值最小的边开始考虑,依次考察权值依次变大的边
- 当前的边要么进入最小生成树的集合,要么丢弃
- 如果当前的边进入最小生成树的集合中不会形成环,就要当前边
- 如果当前的边进入最小生成树的集合中会形成环,就不要当前边
- 考察完所有边之后,最小生成树的集合也得到了
6 最短路径
某点到某点的最短路径。
6.1 Floyd算法
弗洛伊德算法。
特点:多源算法,一次性计算出所有点之间相互的最短距离,可以处理负权边。
Floyd算法的核心是动态规划,阐述为:i到j的最短距离要么等于i直接到j的距离,要么等于i间接到j的距离。
即: \[ dis[i][j] = Math.Min(dis[i][j],dis[i][k]+dis[k][j]); \]
1 | for(int k=0;k<n;k++){ |
path[0,4] = 3。
path[0,3] = 0,结束,那么路径为0->3->4。
6.2 Bellman-Ford算法
特点:单源算法,动态规划,可以处理负权边。
松弛操作:只经过一条边可以到达哪些点。
6.3 Dijkstra算法
迪杰斯特拉算法。
单源算法,不可以处理负权边,因为dijkstra的本质是贪心,如果出现负权边,那么已经完成过最短路径更新的点可能被推翻。
不断选择离已经相连的点最近的点。
6.4 区别
| Floyd | Bellman-Ford | Dijkstra | |
|---|---|---|---|
| 时间复杂度 | \(O(V^3)\) | \(O(V*E)\) | \(O(V^2)\) |
| 空间复杂度 | \(O(V^2)\) | \(O(V)\) | \(O(V^2)\) |
| 源数 | 多源 | 单源 | 单源 |
| 负权处理能力 | 负权边 √ 负权回路 × | 负权边 √ 负权回路 × | 负权边 × 负权回路 × |