1 邻接矩阵
1.1 定义
int G[N][N]
如果没有特殊指定, 下文的nnn表示点数, eee表示边数
g[i][j]g[i][j]g[i][j]表示点iii到jjj边的权值
当iii和jjj之间连边时, g[i][j]=1g[i][j] = 1g[i][j]=1或权值
当iii和jjj之间不连边时, g[i][j]=0g[i][j] = 0g[i][j]=0或∞\infty∞
例:
这个图的邻接矩阵:
G=[∞254∞∞21∞∞∞3∞∞∞∞]G = \left[
\begin{matrix}
\infty & 2 & 5 & 4 \\
\infty & \infty & 2 & 1 \\
\infty & \infty & \infty & 3 \\
\infty & \infty & \infty & \infty
\end{matrix}
\right]G=∞∞∞∞2∞∞∞52∞∞413∞
1.2 参考代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n, e;
int g[105][105];
int main(){
// 初始化为很大的数, 不带权的图第二个空填0
memset(g, 0x3f, sizeof(g));
scanf("%d %d", &n, &e);
int a, b, c;
for(int i = 1;i <= e;i++){
scanf("%d %d %d", &a, &b, &c);
g[a][b] = c; // 不带权的图: g[a][b] = 1
g[b][a] = c; // 无向图的对称性, 有向图不要这一句
}
return 0;
}
2 链式前向星
邻接矩阵的空间复杂度为O(n2)O(n^2)O(n2), 数据量大的时候会爆内存
2.1 定义
邻接表由333个数组模拟, 有权值还要多加一个数组, 以下为数组表示的内容:
head[N]head[N]head[N]: 这个点连接的第一条边, head[i]=−1head[i] = -1head[i]=−1表示iii没有连边
to[E]to[E]to[E]: 这条边到达的点
ne[E]ne[E]ne[E]: 即nextnextnext, 这条边起点连接的下一条边, ne[i]=−1ne[i] = -1ne[i]=−1表示, 这已经是边iii起点的最后一条连边
w[E]w[E]w[E]: 这条边的权值, 只在有权值的图中
2.2 加边
这个图的邻接表:
数组名 \ 下标 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
head | 3 | 5 | 6 | -1 | ||
to | 2 | 3 | 4 | 3 | 4 | 4 |
ne | -1 | 1 | 2 | -1 | 4 | -1 |
w | 2 | 5 | 4 | 2 | 1 | 3 |
2.3 输出
可以看到, 每一个点iii连接的第一条边是j=headij = head_ij=headi, 到达的点和边权存在toj,wjto_j, w_jtoj,wj里, 而iii的下一条边就是j=nejj=ne_jj=nej, 直到j=−1j = -1j=−1结束
2.4 参考代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int n, e;
int head[105], to[105], ne[105], w[105], idx = 1;
void add(int a, int b, int c){
to[idx] = b;
w[idx] = c;
ne[idx] = head[a];
head[a] = idx++;
}
int main(){
memset(head, -1, sizeof(head)); // 初始化
scanf("%d %d", &n, &e);
int a, b, c;
for(int i = 1;i <= e;i++){
scanf("%d %d %d", &a, &b, &c);
add(a, b, c); // 不带权的图: add(a, b)
add(b, a, c); // 无向图的对称性, 有向图不要这一句
}
for(int i = 1;i <= n;i++){
printf("%d: ", i);
for(int j = head[i];j != -1;j = ne[j]){
printf("%d(%d)", to[j], w[j]);
}
}
return 0;
}
3 邻接表
对于链式前向星有时一个点连接的边太多, 例如在稠密图中大量判断哪些点是否相连, 有可能TLE
3.1 定义和使用
邻接表是由一个二维vectorvectorvector数组构成的, vectorvectorvector的优点是可以动态修改大小
g[i][j]g[i][j]g[i][j]表示第iii个点第jjj条边连接的点, 如果有权值
加边: g[a].push_back({b, c})
访问: 外层遍历点iii, 内层遍历j:0∼g[i].size()−1j: 0 \sim g[i].size()-1j:0∼g[i].size()−1, g[i][j]g[i][j]g[i][j]就是连接的点
3.2 参考代码
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
int n, e;
vector<pair<int, int>> g[105];
int main(){
g.clear();
scanf("%d %d", &n, &e);
int a, b, c;
for(int i = 1;i <= e;i++){
scanf("%d %d %d", &a, &b, &c);
g[a].push_back({b, c}); // 不带权的图: add(a, b)
g[b].push_back({a, c}); // 无向图的对称性, 有向图不要这一句
}
for(int i = 1;i <= n;i++){
printf("%d: ", i);
for(int j = 0;j < g[i].size();j++){
printf("%d(%d)", g[i][j].first, g[i][j].second);
}
}
return 0;
}
4 总结
存储结构 | 数据结构 | 空间复杂度 | 单次访问时间 |
---|---|---|---|
邻接矩阵 | 二维数组 | O(n2) | O(1) |
邻接表 | 4个一维数组 | O(n) | 平均每个点连边数量 |
前向星 | 二维vector | O(n+e) | vector下标访问速度 |
Y1.8 Nov.11.2023 Sat.