「学习笔记」存图
我们既然正在学习 图论 ,那么我们就 必须 掌握几种 存图方式 。
PS:本文记点 \(u\) 的 出度 为 \(degree^{+(u)}\)
1. 直接存图
用一个 结构体数组 \(edge_i\) 来存图,结构体中包含这条边 \((u,v,w)\) 的 源点、汇点,权值 。
显然,此方法 空间复杂度 为 \(O(m)\) ,查询某条边的时间复杂度 为 \(O(m)\) 。
2. 邻接矩阵
邻接矩阵是一种 方便且容易理解 的存图方式。
其本质是一个数组 \(grath_{i,j}\space(1\le i,j\le n)\) ,表示从点 \(i\) 到点 \(j\) 的权值。
显然,邻接矩阵的 空间复杂度 是 \(O(n^2)\) 。
如此之高的空间复杂度,只要 \(n\ge 10^4\) , 内存就会超出限制 ,而且也不能应用在 有重边 的情况下。
但是,
在 稠密图 ( \(m\approx n^2\) ) ,下,它比 直接存图的方式更优 ,因为要查询某一条边,两者的时间复杂度分别为 \(O(m)\) 和 \(O(1)\) 。
而接下来介绍的方法,比前两者 都更优秀 。
3. 邻接表 (链式前向星)
这种方法使用一个支持动态增加元素的数据结构构成的数组, 如使用 struct edge {int v,w,nxt;} grath[MAXM]; 来存边,其中 grath[cnt] 存储的是第 \(cnt\) 条边的相关信息 (去向、权等)。
而 链式前向星 就是用 链表 实现的 邻接表 。
此方式的时空复杂度 极其优秀:
查询是否存在边 \((u,v)\) : \(O(degree^{+(u)})\) ,可优化到 \(O(\log(degree^{+(u)}))\)
遍历点 \(u\) 的所有出边 (即以点 \(u\) 为源点的边): \(O(degree^{+(u)})\)
遍历整张图: \(O(n+m)\)
空间复杂度: \(O(m)\)
总结:
如果是在 稠密图且没有重边 的情况下,可以使用 邻接矩阵 。
如果要像 \(Kruskal算法\) 一样按照 边权大小 进行排序,则 必须使用直接存图 。
一般情况下,请使用 邻接表 或 链式前向星 。