考点频率 :★★★★☆(选择题常考,是理解图遍历和算法的基础)
难度 :⭐⭐⭐
建议:重点掌握两种存储方式的核心区别(空间复杂度、操作效率),能根据场景判断该用哪种
1️⃣ 为什么需要图的存储结构?
前一篇文章我们学习了图的基本概念------顶点、边、有向图、无向图......但这些都是逻辑结构 。要把图存到计算机里,需要一种具体的物理存储方式。
图的存储方式有很多种,软考中最常考的是两种:
- 邻接矩阵(Adjacency Matrix):用二维数组表示顶点之间的边关系
- 邻接表(Adjacency List):用数组+链表表示顶点之间的边关系
打个比方 :邻接矩阵像地铁线路的全连接表 ------行和列都是站点,交叉点标记是否连通(1/0),查任意两个站点是否直达非常快,但表格很大。邻接表像每个站点的"出口指示牌"------每个站点只列出它直接连通的其他站点,省空间,但查两个站点是否连通需要翻牌子。
2️⃣ 邻接矩阵(Adjacency Matrix)
2.1 核心思想
用一个 n×nn \times nn×n 的二维数组 AAA 来表示图中 nnn 个顶点之间的边关系。
表示规则:
- 对于无向图 :Aij=1Aij = 1Aij=1 表示顶点 iii 和 jjj 之间有边,否则为 0
- 对于有向图 :Aij=1Aij = 1Aij=1 表示从顶点 iii 到 jjj 有一条有向边,否则为 0
- 对于带权图 :Aij=wAij = wAij=w(权值),无连接时用 ∞\infty∞ 表示
2.2 示例
无向图(3个顶点,边:1-2,2-3):
邻接矩阵(n=3):
1 2 3
1 0 1 0
2 1 0 1
3 0 1 0
特点 :无向图的邻接矩阵是对称矩阵 (Aij=AjiAij = AjiAij=Aji)。
有向图(3个顶点,边:1→2,2→3,3→1):
邻接矩阵(n=3):
1 2 3
1 0 1 0
2 0 0 1
3 1 0 0
2.3 邻接矩阵的优缺点
| 优点 | 缺点 |
|---|---|
| 判断两个顶点之间是否有边 :O(1)O(1)O(1)(直接查矩阵) | 空间复杂度 :O(n2)O(n^2)O(n2)(无论图有多少条边,都要 n2n^2n2 空间) |
| 求某个顶点的度 :O(n)O(n)O(n)(统计该行/列中1的个数) | 对于稀疏图,浪费大量空间(矩阵中很多0) |
| 实现简单,代码直观 | 对于带权图 ,需要处理 ∞\infty∞ 值 |
3️⃣ 邻接表(Adjacency List)
3.1 核心思想
邻接表是一种链式存储 结构。对于图中的每个顶点,用一个链表(或数组)来存储所有与它相邻的顶点。
数据结构:
-
表头数组
head[1..n]:每个元素指向一个链表,存储从该顶点出发的所有边 -
链表节点:存储邻接顶点的编号,以及指向下一个节点的指针
无向图(3个顶点,边:1-2,2-3):
head[1] → 2
head[2] → 1 → 3
head[3] → 2
3.2 邻接表的优缺点
| 优点 | 缺点 |
|---|---|
| 空间复杂度 :O(n+e)O(n + e)O(n+e)(eee 为边数),适合稀疏图 | 判断两个顶点之间是否有边 :O(degree)O(degree)O(degree)(需要遍历链表) |
| 求某个顶点的度 :O(1)O(1)O(1)(对于有向图出度 O(1)O(1)O(1),入度需遍历所有节点或建逆邻接表) | 实现比邻接矩阵复杂(需要链表操作) |
| 遍历所有邻接顶点 :O(degree)O(degree)O(degree),效率高 | 对于稠密图,链表存储指针开销较大 |
3.3 有向图的邻接表
对于有向图,通常只存储出边:
有向图(3个顶点,边:1→2,2→3,3→1):
head[1] → 2
head[2] → 3
head[3] → 1
如果需要经常计算入度 ,可以建立逆邻接表(存储指向该顶点的所有顶点)。
4️⃣ 邻接矩阵 vs 邻接表(完整对比表,重点!)
| 对比项 | 邻接矩阵 | 邻接表 |
|---|---|---|
| 空间复杂度 | O(n2)O(n^2)O(n2) | O(n+e)O(n + e)O(n+e) |
| 判断边是否存在 | O(1)O(1)O(1)(直接查) | O(degree)O(degree)O(degree)(需遍历链表) |
| 求某顶点的度(无向图) | O(n)O(n)O(n)(统计一行) | O(1)O(1)O(1)(链表长度) |
| 求某顶点的出度(有向图) | O(n)O(n)O(n)(统计一行) | O(1)O(1)O(1)(链表长度) |
| 求某顶点的入度(有向图) | O(n)O(n)O(n)(统计一列) | O(n+e)O(n + e)O(n+e)(需遍历所有链表) |
| 适合图类型 | 稠密图(边多) | 稀疏图(边少) |
| 实现复杂度 | 简单 | 较复杂 |
| 存储方式 | 二维数组 | 数组 + 链表 |
5️⃣ 如何选择?(软考常考)
| 场景特征 | 推荐方式 | 理由 |
|---|---|---|
| 图边数很多(稠密图) | 邻接矩阵 | 矩阵中大部分位置都有值,空间利用率高 |
| 图边数很少(稀疏图) | 邻接表 | 只存储存在的边,节省空间 |
| 需要频繁判断两个顶点之间是否有边 | 邻接矩阵 | O(1)O(1)O(1) 判断,邻接表需要遍历链表 |
| 需要频繁遍历某个顶点的所有邻接顶点 | 邻接表 | 直接遍历链表,效率高 |
6️⃣ 经典例题
例题1 :一个有 nnn 个顶点、eee 条边的无向图,若采用邻接表存储,则表头数组的大小为( ),所有链表的节点总数为( )。
A. nnn;2e2e2e
B. nnn;eee
C. n+1n+1n+1;2e2e2e
D. n+1n+1n+1;eee
解析 :无向图中,每条边在邻接表中出现两次 (两个方向各一次),因此链表节点总数为 2e2e2e。表头数组大小为 nnn。选 A。
例题2:以下关于邻接矩阵和邻接表的叙述中,正确的是( )。
A. 邻接矩阵适合存储稀疏图
B. 邻接表判断两个顶点之间是否有边的效率高于邻接矩阵
C. 邻接矩阵的空间复杂度为 O(n+e)O(n+e)O(n+e)
D. 邻接表适合存储稀疏图
解析 :A错误------邻接矩阵适合稠密图 ;B错误------邻接矩阵判断边是否存在是 O(1)O(1)O(1),邻接表是 O(degree)O(degree)O(degree),邻接矩阵更快;C错误------邻接矩阵空间复杂度为 O(n2)O(n^2)O(n2)。D正确。选 D。
例题3 (判断):在有向图的邻接表中,第 iii 个链表的长度等于顶点 iii 的入度。( )
解析 :错误。在有向图的邻接表中,第 iii 个链表存储的是从顶点 iii 出发 的边,其长度等于顶点 iii 的出度,而不是入度。
7️⃣ 记忆口诀
邻接矩阵二维表,O(1)O(1)O(1) 查边效率高,空间 O(n2)O(n^2)O(n2) 只适稠密图。
邻接表用链表存,只存存在的边,空间 O(n+e)O(n+e)O(n+e) 适合稀疏图。
无向图中边存两次,表头大小就是顶点数。
8️⃣ 小测验(评论区对答案)
一个有 nnn 个顶点、eee 条边的有向图 ,采用邻接表存储时,所有链表的节点总数为( )。
A. nnn
B. eee
C. 2e2e2e
D. n+en+en+e
。
🔔 本专栏日更,点击头像 → 专栏《软考中级高频考点》订阅,第一时间接收新内容
#软考中级 #软件设计师 #邻接矩阵 #邻接表 #图存储 #数据结构 #软考备考