图的存储
834 字
4 分钟
图的存储

图的存储
图论题的第一步通常不是跑算法,而是先把图存起来。
不同存储方式适合不同场景。常见方式有:
- 邻接矩阵
- 边集数组
- 邻接表
- 链式前向星
1、先理解图里的基本元素
图由点和边组成。
如果有一条边:
a -> b,权重为 c那么存图时至少要记录:
起点 a终点 b权重 c如果是无向图,一条边 a - b 通常要存两次:
a -> bb -> a2、邻接矩阵
邻接矩阵用二维数组存边:
int g[N][N];如果 g[a][b] = c,表示从 a 到 b 有一条权重为 c 的边。
初始化时一般写:
memset(g, 0x3f, sizeof g);for (int i = 1; i <= n; i++) g[i][i] = 0;加边:
g[a][b] = min(g[a][b], c);邻接矩阵的优点是查询两点之间是否有边很快,O(1)。
缺点是空间复杂度是 O(n^2),点数大时开不下。
3、边集数组
边集数组就是把每条边直接存成一个结构体:
struct Edge { int a, b, w;};
vector<Edge> edges;加边:
edges.push_back({a, b, w});这种写法适合 Kruskal 最小生成树,因为 Kruskal 需要把所有边拿出来排序。
4、vector 邻接表
如果要经常遍历某个点的所有出边,可以用邻接表:
vector<pair<int, int>> g[N];其中:
g[a] 存从 a 出发的所有边pair 的 first 是终点,second 是权重加边:
g[a].push_back({b, c});遍历:
for (auto item : g[a]) { int b = item.first; int c = item.second;}这种写法直观,适合学习和一般题目。
5、链式前向星
竞赛模板里经常使用链式前向星,本质上是用数组模拟邻接表。
const int N = 100010;const int M = 200010;
int h[N], e[M], w[M], ne[M], idx;含义:
h[a]:点 a 的第一条边编号e[i]:第 i 条边的终点w[i]:第 i 条边的权重ne[i]:与第 i 条边同起点的下一条边编号idx:当前边编号初始化:
memset(h, -1, sizeof h);加边:
void add(int a, int b, int c) { e[idx] = b; w[idx] = c; ne[idx] = h[a]; h[a] = idx++;}遍历点 a 的所有出边:
for (int i = h[a]; i != -1; i = ne[i]) { int b = e[i]; int c = w[i];}6、完整链式前向星模板
#include <bits/stdc++.h>using namespace std;
const int N = 100010;const int M = 200010;
int n, m;int h[N], e[M], w[M], ne[M], idx;
void add(int a, int b, int c) { e[idx] = b; w[idx] = c; ne[idx] = h[a]; h[a] = idx++;}
int main() { cin >> n >> m; memset(h, -1, sizeof h);
while (m--) { int a, b, c; cin >> a >> b >> c; add(a, b, c); }
for (int a = 1; a <= n; a++) { cout << a << ":"; for (int i = h[a]; i != -1; i = ne[i]) { cout << " -> " << e[i] << "(" << w[i] << ")"; } cout << endl; }
return 0;}7、怎么选择存图方式
点数小、要快速查边:邻接矩阵要给所有边排序:边集数组要遍历每个点的出边:邻接表追求数组模板和性能:链式前向星8、原图笔记
下面保留原来的图示,方便对照复习。
邻接矩阵

边集数组

邻接表

链式邻接表

链式前向星

总结
图的存储方式会影响后面算法的写法。学习 Dijkstra、Prim、拓扑排序这类算法时,建议优先掌握链式前向星,因为它和竞赛模板结合得最紧。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!














