

struct node
{
int y,z;
node(int a,int b):y(a),z(b){};
};
vector<node> e[100];//邻接表
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
e[x].push_back( (node){y,z} );
//e[y].push_back((node){x,z});
//存入无向图
}
对第一种方法有种优化就是对每个点所对应的边的向量进行预估。例如有m条有向边,n个点,那么每个向量需要reserve(6*(m/n)/5);一般这样存储效率会有显著提高。
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<vector<size_t>> graph(5);
graph[0].push_back(1);//V0->V1.
graph[1].push_back(4);//V1->V4.
graph[1].push_back(0);//V1->V0.
graph[2].push_back(1);//V2->V1.
graph[2].push_back(3);//V2->V3.
graph[3].push_back(0);//V3->V0.
graph[4].push_back(3);//V4->V3.
//假定要访问点1.
for(const auto &ele:graph[1])//对于全部属于graph[1]这个容器的元素
std::cout<<ele<<'';
std::cout<<std::endl;
//程序运行后输出40,表示点1连接了4点和0点。
return 0;
}
第二种(使用map,set):
方法太多,不再举例了。
然而我们这样存图是不够的,对于无向图而言,可能存在一条边需要知道自己的反向边的位置。例如欧拉回路,例如网络流都是需要的。
第二种方法由于std::map<std::size_t,std::set<std::size_t>> graph;只是离散化后的邻接矩阵。对于这种存图方法,访问反向边则非常简单,例如我访问的是a->b这样一条边。那么只需要graph[b].count(a);就可以访问其反向边b->a。
然而对于第一种方法,我们没有办法解决反向边的互相访问题。
所以我们对于这种图需要存图修正。代码如下:
对于list容器可以直接存迭代器的,但是存图时也得考虑a是否等于b.forward_list存反向边的图就不好,因为用链表存图就是需要存完图后插入删除,假定一个元素前面的元素被删除了,那么根本无法访问反向边!!!!
感觉存图没问题了?NO!!!!还有一种图更奇葩,那就是对于每个点中的边得排序又得知道反向边的那种图。USACO上有个题目叫做骑马修栅栏,那个题要求字典序输出。数据量很小,以至于可以直接矩阵存图,但是我们考虑如何数据量大,这种方法就不行了。如果用第二种方法(std::map<std::size_t,std::set<std::size_t>>)存图,绝对可以,但是相对常数较大。如果我们考虑顺序容器去存储则比较快一些。
方法就是先用边集表存图,然后每条边a,b得优先以std::min(a,b)为第一关键字再按std::max(a,b)为第二关键字排序,再按照修正后的存图方法存图即可。具体代码见nocow上骑马修栅栏那题lgeecn发的题解和代码。
如果使用list存图可以先存图再用list.sort()函数进行排序,不过似乎效率会差一些,毕竟相对于vector,list常数太大了,达到6倍以上。
存图真心不简单,因为真正用的时候你可能会遇到各种问题,但是你可以加以思考,进行容器搭配使用,即可解决。
#include<map>
#include<set>
#include<iostream>
#include<cstddef>
#include<map>
#include<set>
intmain()
{
std::map<std::size_t,std::set<std::size_t>>graph;
graph[0].insert(1);//V0->V1.
graph[1].insert(4);//V1->V4.
graph[1].insert(0);//V1->V0.
graph[2].insert(1);//V2->V1.
graph[2].insert(3);//V2->V3.
graph[3].insert(0);//V3->V0.
graph[4].insert(3);//V4->V3.
//假定要访问点1.
for(constauto&ele:graph[1])//对于全部属于graph[1]这个容器的元素
std::cout<<ele<<'';
std::cout<<std::endl;
//程序运行后输出04,表示点1连接了0点和4点。对map,set里的元素进行遍历是有序的
return0;
}
#include<vector>
#include<iostream>
#include<utility>
#include<cstddef>
intmain()
{
std::vector<std::vector<std::pair<std::size_t,std::size_t>>>graph(5);//每条边的第二个元素不能是迭代器!!会失效!!!!必须是下标!!!
//比如有一条a-b的边,我们得让它实现互访。我们这里假定a=2,b=4;
std::size_ta(2),b(4);
graph[a].push_back(std::make_pair(b,graph[b].size()+(a==b)));//Va->Vb.需要判定a是否等于b.
graph[b].push_back(std::make_pair(a,graph[a].size()-1));//Vb->Va,这个不需要判定a是否等于b.
//访问反向边的方法.
for(constauto&ele:graph[a])//访问点a
graph[ele.first][ele.second];//这就是边ele的反向边了
return0;
}
本文探讨了图数据结构的多种存储方式,包括邻接表、边集表及使用std::map和std::set存储无向图和有向图的方法。详细介绍了如何优化存储效率,解决反向边访问问题,以及在不同场景下选择合适的数据结构进行图存储和遍历。
1984

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



