1. 图的邻接表存储结构 表头结点后面跟的邻接结点的排列先后顺序有要求吗
可以,这个没有什么区别。
四种表示图的方法:
1.邻接矩阵
2.邻接表
3.邻接多重表
4.十字链表
2. 求大虾解答【数据结构】判断题
1对。
2对
3错;数组中尽量避免插入和删除操作
4对
5错
6对;LBR和LRB没B时遍历时一样。L左R右B根
7.错
8对。
9错
10错。
3. 邻接表的表示法
注意:
n个顶点e条边的无向图的邻接表表示中有n个顶点表结点和2e个边表结点。(换句话说,每条边(i,j)在邻接表 中出现两次:一次在关于i的邻接表中,另一次在关于j的邻接表中) 对于有向图,vi的邻接表中每个表结点都对应于以vi为始点射出的一条边。因此,将有向图的邻接表称为出边表。
【例】有向图G6如下图所示,其中顶点v1的邻接表上两个表结点中的顶点序号分别为0和4,它们分别表示从v1射出的两条边(简称为v1的出边):<v1,v0>和<v1,v4>。
注意:
n个顶点e条边的有向图,它的邻接表表示中有n个顶点表结点和e个边表结点。(因为有向图是单向的) 在有向图中,为图中每个顶点vi建立一个入边表的方法称逆邻接表表示法。
入边表中的每个表结点均对应一条以vi为终点(即射入vi)的边。
【例】G6的逆邻表如上面(b)图所示,其中v0的入边表上两个表结点1和3分别表示射人v0的两条边(简称为v0的入边):<v1,v0>和<v3,v0>。
注意:
n个顶点e条边的有向图,它的逆邻接表表示中有n个顶点表结点和e个边表结点。
3.邻接表的形式说明。
邻接表是一个二维容器,第一维描述某个点,第二维描述这个点所对应的边集们。
实现邻接表的方法绝对有100种以上。即使是前向星这种东西也是邻接表,因为它还是描述某个点和这个点所对应的边集们.
我们说说常用的邻接表存图法(静态的array就不说了.)必须有开O1以及以上编译的条件,不然没有测试的效率无任何意义。
第一维是描述点的。可以用vector,list,forward_list,deque,map,multimap,unordered_map,unordered_multimap等(一般不能用set,mutiset,unordered_set,unordered_multiset).
按照你的要求去选择。一般来讲存完图以后不涉及点的加入与删除优先使用vector.map,multimap,unordered_map,unordered_multimap.
第二维是描述这个点的边集,可以用全部的容器。也是的一般来讲存完图以后,不涉及点的加入与删除优先使用vector,空间充足可以考虑deque.涉及点的删除用forward_list或者是list,map,multimap,unordered_map,unordered_multimap.
对于这个图存储的方法举例(对于上面的图):
第一种: #include<vector>#include<iostream>usingnamespacestd;intmain(){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(constauto&ele:graph[1])//对于全部属于graph[1]这个容器的元素std::cout<<ele<<'';std::cout<<std::endl;//程序运行后输出40,表示点1连接了4点和0点。return0;}对第一种方法有种优化就是对每个点所对应的边的向量进行预估。例如有m条有向边,n个点,那么每个向量需要reserve(6*(m/n)/5);一般这样存储效率会有显着提高。
第二种(使用map,set): #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;}方法太多,不再举例了。
然而我们这样存图是不够的,对于无向图而言,可能存在一条边需要知道自己的反向边的位置。例如欧拉回路,例如网络流都是需要的。
第二种方法由于std::map<std::size_t,std::set<std::size_t>> graph;只是离散化后的邻接矩阵。对于这种存图方法,访问反向边则非常简单,例如我访问的是a->b这样一条边。那么只需要graph[b].count(a);就可以访问其反向边b->a。
然而对于第一种方法,我们没有办法解决反向边的互相访问题。
所以我们对于这种图需要存图修正。代码如下: #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])//访问点agraph[ele.first][ele.second];//这就是边ele的反向边了return0;}对于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倍以上。
存图真心不简单,因为真正用的时候你可能会遇到各种问题,但是你可以加以思考,进行容器搭配使用,即可解决。
4. 关于数据结构中邻接表的问题
邻接表是图的一种链接存储结构。在邻接表中,对图中每个顶点建立一个带头结点的单链表,所有的头结点构成一个数组,第i个单链表中的结点表示依附于顶点vi的边。也就是说指的是点,表示的是边,因为两点决定了一条边。以下图为例:
与0号点相连的有2条边,一条与1号点相连,一条与3号点相连。所以v0后面有两个结点,前面的序号分别为1、3,3后没有了,为空;
与1号点相连的有3条边,分别与0、2、3号点相连。所以v0后面有3个结点,前面的序号分别为0、2、3,3后没有了,为空;
与2号点相连的有1条边,与1号点相连。所以v0后面有1个结点,前面的序号为1,1后没有了,为空。
对照图好好理解,应该能明白了。
5. 图的邻接表是否没有先后顺序
通常的建表方式无论有向还是无向是不考虑顺序问题的,因为邻接表表示的仅仅是两个节点是否相连以及它们的权值,即使加上顺序也没有什么用处.不过要是为了输出看着好看的话,可以在建表的时候加上类似插入排序的算法结构,让每个节点对应的一串是有序的.
6. c语言,关于邻接表的建立
AdjList 是自定义类型,是char类型,
第二行 typedef将char类型自定义为 VertexType,即VertexType代表了char型,
VertexType a 就相当于 char a;
同理
倒数第五行 typedef将VertexNode自定义为 AdjList[MaxVertexNum]类型,也就是说现在AdjList就代表了一个 “结构体数组” 类型
AdjList adjlist 相当于 VertexNode adjlist[MaxVertexNum]
这里主要是typedef关键字的使用 希望能帮到你
7. 采用邻接表存储的图的深度优先遍历算法类似于二叉树的先序遍历,为什么是先序呢
这是因为图的深度优先遍历算法先访问所在结点,再访问它的邻接点。与二叉树的先序遍历先访问子树的根结点,再访问它的孩子结点(邻接点)类似。图的广度优先遍历算法类似于二叉树的按层次遍历。
先序遍历也叫做先根遍历、前序遍历,可记做根左右(二叉树父结点向下先左后右)。
首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树,如果二叉树为空则返回。
例如,下图所示二叉树的遍历结果是:ABDECF。
(7)领接表存储必须有序嘛扩展阅读:
遍历种类:
一、NLR:前序遍历(Preorder
Traversal
亦称(先序遍历)),访问根结点的操作发生在遍历其左右子树之前。
二、LNR:中序遍历(Inorder
Traversal),访问根结点的操作发生在遍历其左右子树之中(间)。
三、LRN:后序遍历(Postorder
Traversal),访问根结点的操作发生在遍历其左右子树之后。
注意:
由于被访问的结点必是某子树的根,所以N(Node)、L(Left
subtree)和R(Right
subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为
先根遍历、中根遍历和后根遍历。
参考资料来源:网络-先序遍历