408数据结构第6章:图——核心考点、算法对比与AOE网真题

复习位置:数据结构 -> 图

目标:用一篇文章串起图的存储、遍历、最小生成树、最短路径、拓扑排序和关键路径。

说明:为避免复制到 CSDN 后公式乱码,全文以普通文本、代码块和表格为主。


一、图这一章到底考什么

408中"图"这一章看起来算法很多,但实际上可以压缩成六类问题:

复制代码
图怎么存
图怎么遍历
怎么连通所有顶点且代价最小
怎么找两点之间的最短路径
怎么判断任务先后顺序
怎么找工程中的关键活动

它们分别对应:

问题 典型知识点
图怎么存 邻接矩阵、邻接表
图怎么遍历 DFS、BFS
连通所有顶点且总代价最小 最小生成树:Prim、Kruskal
求最短路 Dijkstra、Floyd
求任务执行顺序 AOV网、拓扑排序
求工程最短完成时间 AOE网、关键路径

复习时不要把这些算法混在一起。先判断题目在问哪一类问题,再选择对应算法。


二、图的基本概念

图由顶点集合和边集合组成,通常记为:

复制代码
G = (V, E)

其中 V 表示顶点集合,E 表示边集合。

如果边没有方向,就是无向图;如果边有方向,就是有向图。如果边上带有长度、费用或时间,就是带权图。

1. 顶点的度

无向图中,一个顶点连接了多少条边,它的度就是多少。

复制代码
所有顶点度数之和 = 2 × 边数

因为每条边会被两个端点各计算一次。

有向图中要区分:

复制代码
入度:指向该顶点的边数
出度:从该顶点指出的边数

并且:

复制代码
所有顶点入度之和 = 边数
所有顶点出度之和 = 边数

2. 路径与回路

路径是从一个顶点沿着边到达另一个顶点所经过的顶点序列。若路径起点和终点相同,就形成回路。

如果一个有向图中不存在有向回路,它就是:

复制代码
有向无环图 DAG

拓扑排序和AOE网都建立在DAG上。

3. 连通与强连通

无向图中,任意两个顶点之间都存在路径,称为连通图。

有向图中,任意两个顶点之间都能相互到达,称为强连通图。


三、图的存储结构

1. 邻接矩阵

邻接矩阵用一个二维数组表示顶点之间是否有边。

对于无权图:

复制代码
有边:1
无边:0

对于带权图:

复制代码
有边:边权
无边:无穷大
对角线:0

邻接矩阵的特点是查询两点之间是否有边非常快,只需要访问一次数组,因此时间复杂度是 O(1)。但它始终需要 n × n 的空间,所以空间复杂度是 O(n²),更适合稠密图。

2. 邻接表

邻接表为每个顶点建立一条链表,记录从该顶点出发能够到达的邻接点。

邻接表的空间复杂度为:

复制代码
O(n + e)

其中 n 是顶点数,e 是边数,因此更适合稀疏图。

3. 考试对比

|-----------|-------|----------|
| 比较项目 | 邻接矩阵 | 邻接表 |
| 空间 | O(n²) | O(n+e) |
| 判断两点是否有边 | O(1) | 需要扫描链表 |
| 遍历某顶点所有邻边 | O(n) | 与该顶点度数有关 |
| 适合 | 稠密图 | 稀疏图 |

高频判断:

复制代码
稠密图优先邻接矩阵
稀疏图优先邻接表

四、图的遍历:DFS与BFS

1. 深度优先搜索 DFS

DFS的思路是沿着一条路径不断向深处走,直到走不下去,再回退到上一个顶点继续搜索。

它依赖:

复制代码
递归或栈

大白话理解:

复制代码
先一路走到底,再回头找其他路

2. 广度优先搜索 BFS

BFS先访问起点,再访问与起点距离为1的所有顶点,然后访问距离为2的所有顶点,逐层向外扩展。

它依赖:

复制代码
队列

大白话理解:

复制代码
先把附近一圈走完,再向外走下一圈

3. 复杂度

若使用邻接表:

复制代码
DFS:O(n + e)
BFS:O(n + e)

若使用邻接矩阵:

复制代码
DFS:O(n²)
BFS:O(n²)

4. 非连通图的处理

只从一个顶点开始DFS或BFS,只能访问该顶点所在的连通分量。

如果题目要求遍历整个非连通图,必须在外层再扫描所有顶点:

复制代码
for (i = 0; i < n; i++) {
    if (!visited[i]) {
        DFS(i);
    }
}

这是算法题和选择题中的高频遗漏点。


五、最小生成树:Prim与Kruskal

最小生成树解决的是:

复制代码
在连通无向带权图中,选择n-1条边,
连接全部n个顶点,并使边权总和最小。

注意,它不是最短路径问题。

1. Prim算法

Prim从一个顶点出发,每次选择一条能够把"树内顶点"和"树外顶点"连接起来的最小权值边。

它关注的是:

复制代码
顶点集合逐渐扩大

更适合:

复制代码
稠密图

2. Kruskal算法

Kruskal先按边权从小到大排序,每次选择当前最小且不会形成回路的边,直到选出 n-1 条边。

它关注的是:

复制代码
按边从小到大选择

通常配合并查集判断是否形成回路,更适合:

复制代码
稀疏图

3. 快速区分

复制代码
Prim:从一个点开始扩张
Kruskal:从最小边开始选择

4. 高频陷阱

最小生成树可能不唯一,但最小生成树的总权值一定相同。

若图不连通,则不存在生成树,只能得到生成森林。


六、最短路径:Dijkstra与Floyd

1. Dijkstra算法

Dijkstra用于求:

复制代码
一个源点到其他所有顶点的最短路径

每一轮确定一个当前距离源点最近、且还未被确定的顶点,再用它更新其他顶点的距离。

限制:

复制代码
不能处理负权边

如果题目中出现负权边,就不能直接使用Dijkstra。

2. Floyd算法

Floyd用于求:

复制代码
任意两个顶点之间的最短路径

核心思想是逐个允许顶点作为中间点,并不断更新两点之间的最短距离。

更新公式:

复制代码
D[i][j] = min(D[i][j], D[i][k] + D[k][j])

时间复杂度:

复制代码
O(n³)

3. 快速区分

复制代码
单源最短路:Dijkstra
任意两点最短路:Floyd

4. 不要和最小生成树混淆

最短路径关注的是:

复制代码
某两个顶点之间怎么走最短

最小生成树关注的是:

复制代码
怎样以最小总代价连接所有顶点

二者目标完全不同。


七、AOV网与拓扑排序

AOV网是:

复制代码
用顶点表示活动的有向图

如果活动A必须在活动B之前完成,就画一条:

复制代码
A -> B

AOV网必须是有向无环图,因为如果出现环,就会形成循环依赖。

1. 拓扑排序

拓扑排序的目标是得到一个满足所有先后关系的顶点序列。

标准做法:

复制代码
1. 选择一个入度为0的顶点
2. 输出该顶点
3. 删除该顶点及其所有出边
4. 重复以上过程

如果最终没有输出全部顶点,说明图中存在有向环。

2. 拓扑序列是否唯一

不一定。

如果某一步同时存在多个入度为0的顶点,就可能产生多个合法拓扑序列。


八、AOE网与关键路径

AOE网是本题的核心。

AOE网中:

复制代码
顶点表示事件
边表示活动
边权表示活动持续时间

这与AOV网正好相反:

复制代码
AOV:顶点表示活动
AOE:边表示活动

1. 为什么关键路径是最长路径

AOE网通常表示一个工程。工程中不同活动可以并行进行,但整个工程必须等所有活动都完成以后才能结束。

因此,决定工程完成时间的是从源点到汇点所需时间最长的那条路径。

复制代码
关键路径 = 从源点到汇点的最长路径

不是最短路径。

2. 关键活动

关键路径上的活动称为关键活动。关键活动没有时间余量,一旦延误,整个工程通常也会延误。

3. 事件最早发生时间 ve

从源点开始向后计算:

复制代码
ve[j] = max(ve[i] + 边<i,j>的权值)

因为一个事件必须等所有前置活动都完成,所以取最大值。

4. 事件最迟发生时间 vl

从汇点开始反向计算:

复制代码
vl[i] = min(vl[j] - 边<i,j>的权值)

为了保证整个工程不延期,事件最迟不能超过这个时间,所以取最小值。

5. 活动最早开始时间 e

若活动对应边 <i,j>,则:

复制代码
e = ve[i]

6. 活动最迟开始时间 l

复制代码
l = vl[j] - 边<i,j>的权值

7. 判断关键活动

复制代码
时间余量 = l - e

若:

复制代码
l - e = 0

则该活动是关键活动。


九、典型选择题:关于AOE网的说法

题目:

复制代码
下列关于AOE网的说法正确的是:

A. AOE网是以顶点表示活动的网
B. 从源点到汇点的最短路径称为关键路径
C. 存在关键路径的AOE网一定是有向无环图
D. 缩短关键路径上某个活动的时间,整个工程时间必定缩短

正确答案是:

复制代码
C

这道题可以整段判断。AOE网中顶点表示事件,边表示活动,因此A错误;关键路径决定整个工程的完成时间,所以它应当是源点到汇点的最长路径,而不是最短路径,因此B错误;AOE网表示工程活动之间的先后制约关系,若存在有向环,就会形成循环依赖,工程无法正常启动,因此AOE网必须是有向无环图,C正确;一个AOE网可能同时存在多条关键路径,如果只缩短其中一条关键路径上的某个活动,而另一条关键路径长度不变,那么总工期仍由另一条路径决定,因此D中的"必定缩短"错误。


十、这道题暴露出的思维漏洞

如果这道题完全忘记,说明不是单个知识点遗忘,而是下面几组概念没有建立对应关系:

复制代码
AOV和AOE混淆
最短路径和关键路径混淆
拓扑排序和关键路径混淆
关键活动和普通活动混淆

需要重新建立下面这张表:

|------|--------------|
| 概念 | 核心含义 |
| AOV网 | 顶点表示活动 |
| AOE网 | 边表示活动,顶点表示事件 |
| 拓扑排序 | 给活动安排合法先后顺序 |
| 关键路径 | 源点到汇点的最长路径 |
| 关键活动 | 时间余量为0的活动 |


十一、408图这一章的高频陷阱

1. DFS一定用队列

错误。DFS依赖递归或栈,BFS才使用队列。

2. Dijkstra可以处理负权边

错误。Dijkstra不能处理负权边。

3. 最小生成树就是最短路径树

错误。一个是连接全部顶点的最小总成本,一个是从源点出发到各点最短。

4. 拓扑序列一定唯一

错误。若同时存在多个入度为0的顶点,拓扑序列可能不唯一。

5. AOE网中顶点表示活动

错误。AOE网中边表示活动,顶点表示事件。

6. 关键路径是最短路径

错误。关键路径是源点到汇点的最长路径。

7. 缩短一个关键活动一定缩短总工期

错误。存在多条关键路径时,只缩短其中一条未必改变总工期。


十二、考场快速选算法

看到题目后,可以直接按下面判断:

复制代码
遍历整个图 -> DFS / BFS
无权图最短路径 -> BFS
单源带权最短路径且无负权 -> Dijkstra
任意两点最短路径 -> Floyd
连接全部顶点且总代价最小 -> Prim / Kruskal
判断任务先后顺序 -> 拓扑排序
求工程最短完成时间 -> 关键路径

十三、图这一章的复习顺序

建议按以下顺序复习:

复制代码
1. 图的基本概念
2. 邻接矩阵与邻接表
3. DFS与BFS
4. 最小生成树
5. 最短路径
6. 拓扑排序
7. 关键路径

不要一开始就死记所有伪代码。先把每个算法"解决什么问题、使用什么数据结构、有什么限制"弄清楚,再补计算过程。


十四、一页速记

复制代码
邻接矩阵:O(n²),适合稠密图
邻接表:O(n+e),适合稀疏图

DFS:递归/栈,一路走到底
BFS:队列,一层一层走

Prim:从点扩张
Kruskal:按边权从小到大选

Dijkstra:单源最短路,不能有负权边
Floyd:任意两点最短路,O(n³)

AOV:顶点表示活动,做拓扑排序
AOE:边表示活动,顶点表示事件

关键路径:源点到汇点的最长路径
关键活动:l-e=0

十五、回去重点复习哪里

复制代码
数据结构
-> 图
  -> 图的存储
  -> 图的遍历
  -> 最小生成树
  -> 最短路径
  -> 拓扑排序
  -> 关键路径

当前最需要重点回看的部分是:

复制代码
AOV网与AOE网的区别
拓扑排序的过程
ve、vl、e、l的含义
关键路径和关键活动的判断
相关推荐
变量未定义~1 小时前
DFS之剪枝与优化-小猫爬山、 数独、 木棒
算法
Navigator_Z1 小时前
LeetCode //C - 1187. Make Array Strictly Increasing
c语言·算法·leetcode
龍德明宇2 小时前
如何理解大语言模型的负主体性-龍德明宇
人工智能·算法·大语言模型llm·负主体性·ai存在论
白狐_7982 小时前
408数据结构第6章:迪杰斯特拉(Dijkstra)算法——真题精讲
数据结构·算法
皓月斯语3 小时前
P2858 [USACO06FEB] Treats for the Cows G/S
数据结构·c++·算法·动态规划
白白白小纯3 小时前
树和二叉树(1)
数据结构
旖旎夜光3 小时前
LeetCode 397:整数替换(贪心问题) —— 题解
数据结构·c++·算法·leetcode·贪心算法
奈斯先生Vector3 小时前
2026 开发者效能革命:基于创源AIGC 与开源生态的工程化实践、安全沙箱与代码智能体协同
人工智能·算法·架构·prompt·aigc
依然鸣3 小时前
PTA团体程序设计天梯赛L2真题讲解L2-037-040
c++·经验分享·学习·算法·蓝桥杯·深度优先·pat考试