图的存储结构

1 邻接矩阵

1.1 定义

int G[N][N] 如果没有特殊指定, 下文的nnn表示点数, eee表示边数
g[i][j]g[i][j]g[i][j]表示点iiijjj边的权值
iiijjj之间连边时, g[i][j]=1g[i][j] = 1g[i][j]=1或权值
iiijjj之间不连边时, 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=252413

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 加边

在这里插入图片描述这个图的邻接表:

数组名 \ 下标123456
head356-1
to234344
ne-112-14-1
w254213

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:0g[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)平均每个点连边数量
前向星二维vectorO(n+e)vector下标访问速度

Y1.8 Nov.11.2023 Sat.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值