图的问题越来越多 新手瑟瑟发抖
1.邻接矩阵存图
一般开个
graph[3000][3000] 就 30多MB了
后来遇到的图都是动辄 100000个点的
在基础题用的很多 方便!
缺点非常致命
过高的空间复杂度
对于顶点数V,邻接矩阵存图的空间复杂度高达,顶点数上了一万可以不用考虑这种存图方式了。
对于稀疏图来说,邻接矩阵存图内存浪费太严重,这也是邻接矩阵存图在ACM题目中十分罕见的根本原因。
对于不确定边的查询效率一般
比如,我找个编号为1出发的第一条边我还要一条条边判断是否存在(权值是否为0)。
要O(N)扫一遍
ACM基本不会出这样的裸题
2.邻接表存图
在ACM题目中,动态的数据结构一般是不被推荐的,因为动态开辟内存比较消耗时间,且写起来复杂容易出错。
大部分情况我们使用C++STL里的vector作为链表来实现图的邻接表。
vector <int> G[20000];
vector[x].push_back(y);
vector[y].push_back(x);//无向图要加上
遍历的时候
for(int i=0;i<v[u].size();i++){
//do something
}
可以发现不能通过两点马上确定边,只能遍历查找。
3. 链式前向星
这种存图方式的数据结构主要是边集数组,顾名思义,图的边是用数组来存储的。
当然想要完美表示图结构,光有一个边集数组还不够,还要有一个数组存储指向每一个点的第一条边的“指针”。
而每一条边都需要存储接下来一条边的“指针”,这样就能够像类似邻接表一样方便遍历每一个点的所有边了。
#include <stdio.h>
#include <string.h>
// 最大顶点数
const int V = 100000;
// 最大边数
const int E = 100000;
// 边结构体的定义
struct Edge {
int to; // 表示这条边的另外一个顶点
int next; // 指向下一条边的数组下标,值为-1表示没有下一条边
};
// head[i] 表示顶点`i`的第一条边的数组下标,-1表示顶点`i`没有边
int head[V];
Edge edge[E];
// 链式前向星初始化,只需要初始化顶点数组就可以了
memset(head, -1, sizeof(head));
// 增加边的方式
// 新增边 a -> b,该边的数组下标为`id`
inline void addedge(int a, int b, int id)
{
edge[id].to = b;
edge[id].next = head[a]; // 新增的边要成为顶点`a`的第一条边,而不是最后一条边
head[a] = id++;
return;
}
// 遍历从`a`点出去的所有边
for (int i=head[a]; i!=-1; i=e[i].next) {
// e[i] 就是你当前遍历的边 a -> e[i].to
}
优点
内存利用率高
相比vector实现的邻接表而言,可以准确开辟最多边数的内存,不像vector实现的邻接表有爆内存的风险。
对不确定边的操作方便效率也不错
这点和邻接表一样,不会遍历到不存在的边。
缺点
难于理解,代码较复杂
这种存图方式相对于邻接表来说比较难理解,代码虽然不是很复杂但是不熟练的话写起来也不是方便。
重边不好处理
这点与邻接表一样,只有通过遍历判重。
对确定边的操作效率不高
也与邻接表一样,不能通过两点马上确定边,只能遍历查找。
本文深入探讨了图数据结构的不同存储方式,包括邻接矩阵、邻接表及链式前向星,对比了它们的空间复杂度、操作效率及适用场景,并详细解析了每种方式的特点。
1345

被折叠的 条评论
为什么被折叠?



