1 邻接矩阵
1.1 定义
int G[N][N]
如果没有特殊指定, 下文的
n
n
n表示点数,
e
e
e表示边数
g
[
i
]
[
j
]
g[i][j]
g[i][j]表示点
i
i
i到
j
j
j边的权值
当
i
i
i和
j
j
j之间连边时,
g
[
i
]
[
j
]
=
1
g[i][j] = 1
g[i][j]=1或权值
当
i
i
i和
j
j
j之间不连边时,
g
[
i
]
[
j
]
=
0
g[i][j] = 0
g[i][j]=0或
∞
\infty
∞
例:
这个图的邻接矩阵:
G
=
[
∞
2
5
4
∞
∞
2
1
∞
∞
∞
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 ( n 2 ) O(n^2) O(n2), 数据量大的时候会爆内存
2.1 定义
邻接表由
3
3
3个数组模拟, 有权值还要多加一个数组, 以下为数组表示的内容:
h
e
a
d
[
N
]
head[N]
head[N]: 这个点连接的第一条边,
h
e
a
d
[
i
]
=
−
1
head[i] = -1
head[i]=−1表示
i
i
i没有连边
t
o
[
E
]
to[E]
to[E]: 这条边到达的点
n
e
[
E
]
ne[E]
ne[E]: 即
n
e
x
t
next
next, 这条边起点连接的下一条边,
n
e
[
i
]
=
−
1
ne[i] = -1
ne[i]=−1表示, 这已经是边
i
i
i起点的最后一条连边
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 输出
可以看到, 每一个点 i i i连接的第一条边是 j = h e a d i j = head_i j=headi, 到达的点和边权存在 t o j , w j to_j, w_j toj,wj里, 而 i i i的下一条边就是 j = n e j j=ne_j j=nej, 直到 j = − 1 j = -1 j=−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 定义和使用
邻接表是由一个二维
v
e
c
t
o
r
vector
vector数组构成的,
v
e
c
t
o
r
vector
vector的优点是可以动态修改大小
g
[
i
]
[
j
]
g[i][j]
g[i][j]表示第
i
i
i个点第
j
j
j条边连接的点, 如果有权值
加边: g[a].push_back({b, c})
访问: 外层遍历点
i
i
i, 内层遍历
j
:
0
∼
g
[
i
]
.
s
i
z
e
(
)
−
1
j: 0 \sim g[i].size()-1
j: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.