3.4 图

一、图的定义

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)

思想

  1. 访问起始顶点v
  2. 从v出发,访问v的一个未被访问的邻接点w₁
  3. 从w₁出发,访问w₁的一个未被访问的邻接点w₂
  4. 重复上述过程
  5. 当到达一个没有未被访问的邻接点的顶点时,回溯
  6. 重复上述过程,直到所有顶点都被访问

特点

  • 使用递归或栈实现
  • 类似于树的前序遍历
  • 可能产生递归深度过深的问题

2. 广度优先搜索(BFS)

思想

  1. 访问起始顶点v
  2. 依次访问v的所有未被访问的邻接点
  3. 分别从这些邻接点出发,访问它们的所有未被访问的邻接点
  4. 重复上述过程
  5. 直到所有顶点都被访问

特点

  • 使用队列实现
  • 类似于树的层序遍历
  • 可以找到最短路径

七、最小生成树

1. 定义

最小生成树

  • 对于一个带权连通无向图
  • 生成树的各边权值之和称为生成树的代价
  • 代价最小的生成树称为最小生成树

2. Prim算法

思想

  1. 从任意一个顶点开始
  2. 每次选择一个与当前树连接的最小权值边
  3. 将该边和对应的顶点加入树中
  4. 重复直到包含所有顶点

3. Kruskal算法

思想

  1. 将所有边按权值从小到大排序
  2. 从最小权值的边开始
  3. 如果该边连接的两个顶点不在同一连通分量中
  4. 则加入该边
  5. 重复直到包含所有顶点

八、最短路径

1. Dijkstra算法

适用:单源最短路径(正权值)

思想

  1. 初始化:起点距离为0,其他为∞
  2. 每次选择距离最小的未访问顶点
  3. 更新其邻接点的距离
  4. 重复直到所有顶点都被访问

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³)
相关推荐
I_LPL3 小时前
day49 代码随想录算法训练营 图论专题2
java·算法·深度优先·图论·广度优先·求职面试
小小unicorn3 小时前
[微服务即时通讯系统]语音子服务的实现与测试
c++·算法·微服务·云原生·架构·xcode
xsyaaaan3 小时前
代码随想录Day53图:Floyd算法精讲_ Astar算法精讲_最短路算法总结篇_图论总结
算法·图论
lihihi3 小时前
P10471 最大异或对 The XOR Largest Pair
算法
漫随流水3 小时前
备战蓝桥杯(3)
数据结构·c++·算法·蓝桥杯
song8546011343 小时前
hash和history导航区别 个别服务器为啥不支持 history 模式
服务器·算法·哈希算法
IT猿手3 小时前
多无人机动态避障路径规划研究:基于粒子群优化算法PSO的多无人机动态避障路径规划研究(可以自定义无人机数量及起始点),MATLAB代码
算法·matlab·机器人·无人机·路径规划·动态路径规划
MoonBit月兔3 小时前
MoonBit 0.8.3版本更新
开发语言·人工智能·算法·ai编程·moonbit
xsyaaaan3 小时前
leetcode-hot100-哈希表:1两数之和-49字母异位词分组-128最长连续序列
算法·leetcode·散列表