数据结构:邻接矩阵和邻接表的区别

文章目录


前言

**图是一种重要的非线性数据结构,广泛应用于网络路由、社交关系、交通规划、最小生成树(MST)等场景。**在实际算法实现中,如何高效地存储图的顶点与边,直接影响程序的时间和空间性能。最经典的两种存储结构是邻接矩阵和邻接表:前者基于二维数组,以空间换时间,能够常数时间内判断两点是否相邻;后者基于链表或动态数组,以时间换空间,只存储实际存在的边。理解二者的定义、构成、特点及适用场景,是正确选用图算法(如 Prim、Kruskal、Dijkstra)的基础,也是优化图处理效率的关键所在。


一、邻接矩阵

(一)定义

用一个 N × N 的二维数组(矩阵)表示图 ,matrixij 存储从顶点 i 到顶点 j 的边的权值(无权图可存 1/0)

(二)构成元素

一个二维数组,大小为 N×N(通常用 vector<vector>)。

有边处存权值,无边处存一个极大值(如 INF 或 0,具体视场景而定)

(三)特点

1.无向图

无向图的邻接矩阵是对称的,第i行(列)元素之和,就是顶点i的度(边没有权值,只存0/1的情况下,元素和就是度)

2.有向图

有向图的邻接矩阵则不一定是对称的,第i行(列)元素之和就是顶点i 的出(入)度(边没有权值,只存0/1的情况下)

(四)特别说明

1. 带有权值的情况

如果边带有权值,并且两个节点之间是连通的,上图中的边的关系(1/0)就用权值代替,如果两个顶点不通,可以使用无穷大代替(后面我们实现的时候就要增加一个表示无穷大的模板参数)

2...优缺点

  • 用邻接矩阵存储图的优点是能够快速知道两个顶点是否连通,取到权值
  • 缺陷是如果顶点比较多,边比较少时,矩阵中存储了大量的0成为系数矩阵,比较浪费空间;所以邻接矩阵比较适合存储稠密图(边比较多的图),不适合存储稀疏图(边比较少的图) 而且要求两个节点之间的路径不好求; 还有求一个顶点相连的顶点有哪些也不好求(O(N),这个用邻接表结构存储的话就很好求)。

二、邻接表

(一)定义

用一个长度为 N 的数组,每个元素是一个链表或动态数组,存储该顶点的所有邻接点及边权

(二)组成元素

一个数组(如 vector<vector<pair<int,int>>>),索引代表顶点,每个元素是一个列表

列表中每个条目通常是一个 (邻接点, 边权) 的二元组。

(三)分类

1.无向图

  • 一个顶点与哪些顶点相连,相连的顶点就存到这个顶点对应的链表中,当然如果带权的话也要存上对应边的权值。 (每个顶点都有一个对应的链表,多条链表用一个指针数组就可以维护起来)

  • 注意:无向图中同一条边在邻接表中出现了两次。如果想知道顶点vi的度,只需要知道顶点vi 对应链表集合中结点的数目即可

2.有向图

(四)注意

  1. 适合存储稀疏图(边比较少的图),因为邻接表的话有多少边链表里面就存几个对应的顶点,不需要额外的空间;而上面邻接矩阵不论边多边少都要开一个N*N的矩阵(二维数组),边少的时候那就大部分位置都存的是0

  2. 方便查找从一个顶点连接出去的边有哪些,因为它对应的边链表里面存的就是与这个顶点相连的顶点

  3. 但是不方便确定两个顶点是否相连和获取权值(要遍历其中一个顶点的边链表查找O(N)

三、邻接表和邻接矩阵的区别

(一)空间复杂度的对比

存储方式 空间复杂度 备注
邻接矩阵 O(N²) 无论实际边数多少,始终占据 N×N 空间
邻接表 O(N + M) 只存储实际存在的边,M 是边数

(二)主要操作的效率对比

操作 邻接矩阵 邻接表
查询 (u,v) 是否直接相连 O(1) 直接访问 matrix[u][v] O(degree(u)) 需遍历邻居列表找 v
获取某顶点所有邻居 O(N) 需扫描整行 O(degree(u)) 直接读取列表
添加一条边 O(1) O(1) 追加到列表末尾
删除一条边 O(1) O(degree(u)) 需查找并删除
遍历图中所有边 O(N²) 扫描整个矩阵 O(N+M) 恰好扫描所有真实边

四、在MST中的适用场景

(一)邻接矩阵适合

稠密图(M 接近 N²)

顶点数 N 较小(例如 ≤ 1000)

需要频繁查询任意两点是否相连(如 Floyd-Warshall 算法)

在 MST 中:用于朴素 Prim 算法(O(N²)),当图很稠密时效率极高

(二)邻接表适合

稀疏图(M 远小于 N²,常见 M ≈ N 量级)

顶点数 N 很大(如 10⁵ 级别)

需要频繁遍历邻居的操作(DFS、BFS、Dijkstra、堆优化 Prim 等)
在 MST 中:用于堆优化 Prim 算法(O((N+M) log N)),Kruskal 算法甚至可只存边列表而不依赖完整邻接表


五、总结

邻接矩阵与邻接表各有优劣,不存在绝对"更好"的存储方式,只有"更适合"的场景。邻接矩阵采用 O(N²) 空间,适合顶点数较少(如 N ≤ 1000)且边稠密(M 接近 N²) 的图,其 O(1) 的边查询能力在 Floyd-Warshall 或朴素 Prim 算法中优势明显;但面对稀疏图(M 远小于 N²)时,大量空间被浪费,且遍历所有邻居需要 O(N) 开销。邻接表占用 O(N+M) 空间,更适应顶点数很大(如 10⁵ 级别)或边稀疏(M ≈ N) 的图,能高效遍历邻居(O(degree))、快速完成 BFS/DFS 和堆优化的 Dijkstra/Prim 算法;但判断两点是否直接相连需 O(degree) 时间。在最小生成树问题中,稠密图倾向采用邻接矩阵的朴素 Prim 算法,稀疏图则适用邻接表的堆优化 Prim 或纯边列表的 Kruskal 算法。实践时应根据图的规模、密度以及核心操作(查边、遍历邻居、增删边)的频率做出合理选择。

相关推荐
闪电悠米17 小时前
力扣hot100-56.合并区间-排序详解
数据结构·算法·leetcode·贪心算法·排序算法
yuannl1019 小时前
图的存储方式
数据结构
会编程的小孩20 小时前
初识数据类型以及变量定义
数据结构·算法
梅梅绵绵冰20 小时前
数据结构-时间复杂度
数据结构·算法
cpp_250121 小时前
P6625 [省选联考 2020 B 卷] 卡牌游戏
数据结构·c++·算法·前缀和·贪心·洛谷题解·省选
起予者汝也1 天前
Python 数据结构
开发语言·数据结构·python
Frostnova丶1 天前
(17)LeetCode 41. 缺失的第一个正数
数据结构·算法·leetcode
程序猿乐锅1 天前
【数据结构与算法 | 第二篇】 双链表的增删改查
数据结构
牧以南歌〆1 天前
数据结构<一>顺序表
c语言·数据结构·算法
玖玥拾2 天前
C++ 数据结构 八大基础排序算法专题
数据结构·c++·算法·排序算法