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

文章目录


前言

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


一、邻接矩阵

(一)定义

用一个 N × N 的二维数组(矩阵)表示图 ,matrix[i][j] 存储从顶点 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 算法。实践时应根据图的规模、密度以及核心操作(查边、遍历邻居、增删边)的频率做出合理选择。

相关推荐
承渊政道1 小时前
【贪心算法】(经典实战应用解析(二):最⻓递增⼦序列、递增的三元⼦序列、最⻓连续递增序列、买卖股票的最佳时机、买卖股票的最佳时机II)
数据结构·c++·学习·算法·leetcode·贪心算法·哈希算法
東隅已逝,桑榆非晚1 小时前
深⼊理解指针(3)
c语言·数据结构·笔记·算法·排序算法
難釋懷10 小时前
Redis数据结构-Set结构
数据结构·redis·bootstrap
如何原谅奋力过但无声13 小时前
【灵神高频面试题合集06-08】反转链表、快慢指针(环形链表/重排链表)、前后指针(删除链表/链表去重)
数据结构·python·算法·leetcode·链表
平行侠13 小时前
037插入排序 - 整理扑克牌的算法
数据结构·算法
,,?!,15 小时前
数据结构算法-排序算法
数据结构·算法·排序算法
‎ദ്ദിᵔ.˛.ᵔ₎16 小时前
C++哈希表
数据结构·c++·散列表
阿旭超级学得完17 小时前
C++11(初始化)
java·开发语言·数据结构·c++·算法
云淡风轻~窗明几净17 小时前
关于角谷猜想的五行小猜想
数据结构·算法