一、图的定义
1. 基本概念
定义:一个图G(Graph)是由两个集合V和E所组成的。
- V:有限的非空顶点集合
- E:用顶点表示的边集合
表示:G = (V, E)
- V(G):图G的顶点集
- E(G):图G的边集
特点:
- 一个顶点集合与连接这些顶点的边的集合可以唯一表示一个图
- 在图中,数据元素用顶点表示
- 数据元素之间的关系用边表示
二、图的分类
1. 有向图
定义:图中每条边都是有方向的。
表示:
- 从顶点vᵢ到顶点vⱼ表示为
<vᵢ, vⱼ> - 从顶点vⱼ到顶点vᵢ表示为
<vⱼ, vᵢ> - 有向边也称为弧
- 起点称为弧尾
- 终点称为弧头
示例:
1
/|\
/ | \
2 3 4
↑ ↓
└──┘
顶点集合:V(a) = {1, 2, 3, 4}
边的集合:E(a) = {<1,2>, <1,3>, <4,2>, <1,4>, <4,1>}
2. 无向图
定义:图中每条边都是无方向的。
表示:
- 顶点vᵢ和vⱼ之间的边用
(vᵢ, vⱼ)表示 - 在无向图中,
(vᵢ, vⱼ)和(vⱼ, vᵢ)表示的是同一条边
示例:
1
/|\
/ | \
2 3 5
\ | /
\|/
4
顶点集合:V(b) = {1, 2, 3, 4, 5}
边的集合:E(b) = {(1,2), (1,3), (1,4), (4,5), (2,3), (3,4), (3,5)}
3. 完全图
无向完全图:
- 若一个无向图具有n个顶点
- 每一个顶点与其他n-1个顶点之间都有边
- 共有n(n-1)/2条边
有向完全图:
- 有n个顶点
- 任意两个不同顶点之间都存在方向相反的两条弧
- 弧的数目为n(n-1)
对比:
| 类型 | 顶点数 | 边/弧数 | 公式 |
|---|---|---|---|
| 无向完全图 | n | 边数 | n(n-1)/2 |
| 有向完全图 | n | 弧数 | n(n-1) |
三、图的相关概念
1. 度(Degree)
无向图:
- 顶点的度:与该顶点相关联的边的数目
- 记作TD(v)
有向图:
- 入度(InDegree):以该顶点为弧头的弧的数目,记作ID(v)
- 出度(OutDegree):以该顶点为弧尾的弧的数目,记作OD(v)
- 顶点的度:TD(v) = ID(v) + OD(v)
性质:
-
无向图中,所有顶点的度之和等于边数的2倍
ΣTD(v) = 2|E| -
有向图中,所有顶点的入度之和等于所有顶点的出度之和
ΣID(v) = ΣOD(v) = |E|
2. 路径
定义:顶点vₚ到顶点v_q之间的一条路径是指顶点序列
简单路径:路径中顶点不重复
路径长度:路径上边或弧的数目
回路(环):第一个顶点和最后一个顶点相同的路径
3. 子图
定义:设G = (V, E)和G' = (V', E')是两个图
- 如果V' ⊆ V且E' ⊆ E
- 则称G'是G的子图
4. 连通图
无向图的连通:
- 如果从顶点v到顶点v'有路径存在
- 则称v和v'是连通的
连通图:
- 如果图中任意两个顶点都是连通的
- 则称该图为连通图
连通分量:
- 无向图的极大连通子图
5. 强连通图
定义:有向图中
- 如果对于每一对vᵢ, vⱼ ∈ V
- vᵢ ≠ vⱼ
- 从vᵢ到vⱼ和从vⱼ到vᵢ都存在路径
- 则称该图为强连通图
6. 网
定义:边或弧带有权值的图称为网。
四、图的存储结构
1. 邻接矩阵表示法
(1)无向图的邻接矩阵
定义:设G = (V, E)是具有n个顶点的图
- 顶点编号为1, 2, ..., n
- 则G的邻接矩阵是一个n阶方阵
- 记作A = (aᵢⱼ)ₙ×ₙ
元素定义:
aᵢⱼ = 1 若(vᵢ, vⱼ) ∈ E 或 <vᵢ, vⱼ> ∈ E
aᵢⱼ = 0 其他情况
示例:
无向图:
1
/|\
/ | \
2 3 5
\ | /
\|/
4
邻接矩阵:
1 2 3 4 5
1 [0, 1, 1, 1, 0]
2 [1, 0, 1, 0, 0]
3 [1, 1, 0, 1, 1]
4 [1, 0, 1, 0, 1]
5 [0, 0, 1, 1, 0]
特点:
- 无向图的邻接矩阵是对称矩阵
- 顶点i的度 = 第i行(或第i列)元素之和
- 可以直接判断两个顶点之间是否有边
(2)有向图的邻接矩阵
示例:
有向图:
1
/|\
/ | \
2 3→ 4
↑ ↓
└──┘
邻接矩阵:
1 2 3 4
1 [0, 1, 1, 1]
2 [0, 0, 0, 0]
3 [0, 0, 0, 0]
4 [1, 1, 0, 0]
特点:
- 有向图的邻接矩阵不一定是对称矩阵
- 顶点i的出度 = 第i行元素之和
- 顶点i的入度 = 第i列元素之和
(3)网的邻接矩阵
定义:网的邻接矩阵可以表示为:
aᵢⱼ = wᵢⱼ 若(vᵢ, vⱼ) ∈ E 或 <vᵢ, vⱼ> ∈ E
aᵢⱼ = 0 或 ∞ 其他情况
示例:
网:
V₁
/ \
5 7
/ \
V₂ V₄
| |
4 6
| |
V₃ V₆
\ /
9 1
\ /
V₅
邻接矩阵:
V₁ V₂ V₃ V₄ V₅ V₆
V₁ [ 0, 5, ∞, 7, ∞, ∞]
V₂ [∞, 0, 4, ∞, ∞, ∞]
V₃ [ 8, ∞, 0, ∞, ∞, 9]
V₄ [∞, ∞, 5, 0, ∞, 6]
V₅ [∞, ∞, ∞, 5, 0, ∞]
V₆ [ 3, ∞, ∞, ∞, 1, 0]
2. 邻接链表表示法
(1)基本概念
定义:为图的每一个顶点建立一个单链表
- 第i个单链表中的节点表示依附于顶点vᵢ的边(对于有向图是以vᵢ为尾的弧)
节点类型:
表头节点:
┌──────────┬──────────┐
│ data │ firstarc │
│ (顶点信息)│ (指向第一条边)│
└──────────┴──────────┘
表节点:
┌──────────┬──────────┬──────────┐
│ adjvex │ nextarc │ info │
│(邻接顶点)│(指向下一条边)│(边的信息)│
└──────────┴──────────┴──────────┘
(2)无向图的邻接表
示例:
无向图:
1
/|\
/ | \
2 3 5
\ | /
\|/
4
邻接表:
1 → 2 → 3 → 4 → ∧
2 → 1 → 3 → ∧
3 → 1 → 2 → 4 → 5 → ∧
4 → 1 → 3 → 5 → ∧
5 → 3 → 4 → ∧
特点:
- 无向图的邻接表中:
- 顶点i的度 = 第i个链表中的节点数
- 所有链表中的节点数 = 2 × 边数
(3)有向图的邻接表和逆邻接表
邻接表:
有向图:
0
↙ ↘
1 2
↘ ↙
3
邻接表:
0 → 1 → 2 → ∧
1 → 2 → ∧
2 → 0 → ∧
3 → 0 → ∧
逆邻接表:
逆邻接表:
0 → 2 → 3 → ∧
1 → 0 → ∧
2 → 0 → 1 → ∧
3 → ∧
特点:
- 邻接表:便于查找出度
- 逆邻接表:便于查找入度
(4)网的邻接表
示例:
网:
V₀
│8
↓
V₁
│2
↓
V₂ ← 5 → V₃
邻接表:
V₀ → [V₁,8] → ∧
V₁ → [V₂,2] → ∧
V₂ → [V₃,5] → ∧
V₃ → ∧
五、邻接矩阵 vs 邻接表
1. 存储方式对比
| 比较项 | 邻接矩阵 | 邻接表 |
|---|---|---|
| 存储空间 | O(n²) | O(n + e) |
| 稀疏图 | 浪费空间 | 节省空间 |
| 稠密图 | 合适 | 不太合适 |
| 判断边是否存在 | O(1) | O(degree(v)) |
| 获取邻接点 | O(n) | O(degree(v)) |
| 插入/删除边 | O(1) | O(degree(v)) |
2. 适用场景
邻接矩阵适用于:
- 稠密图(边数较多的图)
- 需要频繁判断边是否存在
- 简单实现
邻接表适用于:
- 稀疏图(边数较少的图)
- 需要遍历所有邻接点
- 节省存储空间
六、图的遍历
1. 深度优先搜索(DFS)
思想:
- 访问起始顶点v
- 从v出发,访问v的一个未被访问的邻接点w₁
- 从w₁出发,访问w₁的一个未被访问的邻接点w₂
- 重复上述过程
- 当到达一个没有未被访问的邻接点的顶点时,回溯
- 重复上述过程,直到所有顶点都被访问
特点:
- 使用递归或栈实现
- 类似于树的前序遍历
- 可能产生递归深度过深的问题
2. 广度优先搜索(BFS)
思想:
- 访问起始顶点v
- 依次访问v的所有未被访问的邻接点
- 分别从这些邻接点出发,访问它们的所有未被访问的邻接点
- 重复上述过程
- 直到所有顶点都被访问
特点:
- 使用队列实现
- 类似于树的层序遍历
- 可以找到最短路径
七、最小生成树
1. 定义
最小生成树:
- 对于一个带权连通无向图
- 生成树的各边权值之和称为生成树的代价
- 代价最小的生成树称为最小生成树
2. Prim算法
思想:
- 从任意一个顶点开始
- 每次选择一个与当前树连接的最小权值边
- 将该边和对应的顶点加入树中
- 重复直到包含所有顶点
3. Kruskal算法
思想:
- 将所有边按权值从小到大排序
- 从最小权值的边开始
- 如果该边连接的两个顶点不在同一连通分量中
- 则加入该边
- 重复直到包含所有顶点
八、最短路径
1. Dijkstra算法
适用:单源最短路径(正权值)
思想:
- 初始化:起点距离为0,其他为∞
- 每次选择距离最小的未访问顶点
- 更新其邻接点的距离
- 重复直到所有顶点都被访问
2. Floyd算法
适用:所有顶点对之间的最短路径
思想:
- 动态规划
- 逐步考虑中间顶点
- 更新最短距离
九、拓扑排序
1. 定义
拓扑排序:对一个有向无环图(DAG)的顶点进行排序
- 使得对于每一条有向边(u, v)
- 顶点u在排序中都出现在顶点v之前
2. 应用
- 任务调度
- 课程安排
- 依赖关系处理
十、总结
1. 图的类型总结
| 类型 | 特点 | 边数 |
|---|---|---|
| 无向图 | 边无方向 | e |
| 有向图 | 边有方向 | e |
| 无向完全图 | 每对顶点都有边 | n(n-1)/2 |
| 有向完全图 | 每对顶点都有两条弧 | n(n-1) |
| 网 | 边带权值 | - |
2. 存储结构选择
| 情况 | 推荐存储方式 |
|---|---|
| 稠密图 | 邻接矩阵 |
| 稀疏图 | 邻接表 |
| 需要快速判断边是否存在 | 邻接矩阵 |
| 需要遍历邻接点 | 邻接表 |
3. 常见算法
| 算法 | 用途 | 时间复杂度 |
|---|---|---|
| DFS | 遍历、路径查找 | O(V + E) |
| BFS | 遍历、最短路径 | O(V + E) |
| Prim | 最小生成树 | O(E log V) |
| Kruskal | 最小生成树 | O(E log E) |
| Dijkstra | 单源最短路径 | O((V + E) log V) |
| Floyd | 所有点对最短路径 | O(V³) |