我们既然正在学习 图论 ,那么我们就 必须 掌握几种 存图方式 。
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. 邻接表 (链式前向星)
这种方法使用一个支持动态增加元素的数据结构构成的数组, 如使用 vector<int>grath[n+1] 来存边,其中 grath[u] 存储的是点 $u$ 的所有出边的相关信息 (终点、边权等)。
而 链式前向星 就是用 链表 实现的 邻接表 。
此方式的时空复杂度 极其优秀:
查询是否存在边 $(u,v)$ : $O(degree^{+(u)})$ ,可优化到 $O(\log(degree^{+(u)}))$
遍历点 $u$ 的所有出边 (即以点 $u$ 为源点的边): $O(degree^{+(u)})$
遍历整张图: $O(n+m)$
空间复杂度: $O(m)$
总结:
如果是在 稠密图且没有重边 的情况下,可以使用 邻接矩阵 。
如果要像 $Kruskal算法$ 一样按照 边权大小 进行排序,则 必须使用直接存图 。
一般情况下,请使用 邻接表 或 链式前向星 。