几种图的存储结构的比较
- 格式:ppt
- 大小:575.00 KB
- 文档页数:18
顺序存储结构、链式存储结构、索引存储结构、散列存储结构在计算机科学中,数据的存储和组织方式对于数据的访问和处理起着至关重要的作用。
不同的存储结构在不同的场景下有着各自的优势和不足。
在本文中,我们将介绍四种常见的存储结构:顺序存储结构、链式存储结构、索引存储结构和散列存储结构,并讨论它们的特点和应用。
首先,我们来介绍一下顺序存储结构。
顺序存储结构是一种连续存储数据的方式,数据元素依次存储在内存中的连续位置上。
顺序存储结构具有存取速度快的优点,可以通过下标直接访问指定位置的数据元素。
对于需要频繁访问数据的场景,顺序存储结构是一个不错的选择。
然而,顺序存储结构的缺点是插入和删除操作比较低效,因为需要移动其他数据元素的位置。
接下来,我们介绍链式存储结构。
链式存储结构使用指针将数据元素连接起来,每个数据元素包含一个指向下一个元素的指针。
链式存储结构相对于顺序存储结构来说,插入和删除操作的效率更高,因为只需要修改指针的指向即可。
链式存储结构还可以动态地分配内存空间,不受限制于固定大小的内存块。
然而,链式存储结构的缺点是访问数据元素的效率比较低,需要通过遍历链表来查找指定元素。
接下来,我们介绍索引存储结构。
索引存储结构是在原始数据之上建立索引的方式。
索引存储结构通过建立一个索引表来存储关键字和指向对应数据的指针。
通过索引表,可以快速地定位到指定关键字对应的数据元素。
索引存储结构适用于需要频繁查找特定数据的场景,能够提高数据的查找效率。
然而,索引存储结构的缺点是需要额外的存储空间来存储索引表,并且在插入和删除数据时需要同时更新索引表。
最后,我们介绍散列存储结构。
散列存储结构是根据数据的关键字直接计算出存储位置的方式。
散列存储结构通过散列函数将关键字映射到存储位置,不需要进行比较和遍历操作。
散列存储结构的优点是可以快速地定位到数据元素,具有较高的存取效率。
然而,散列存储结构可能会出现冲突,即不同的关键字映射到同一个存储位置的情况,需要解决冲突的方法,如链地址法和开放地址法。
图的常⽤存储结构⼀、邻接矩阵 邻接矩阵是简单的也是⽐较常⽤的⼀种表⽰图的数据结构,对于⼀个有N个点的图,需要⼀个N*N的矩阵,这个矩阵的i⾏第j列的数值表⽰点vi到点vj的距离。
邻接矩阵需要初始化,map[i][i] = 0;map[i][j] = INF(i != j),对于每组读⼊的数据vi,vj,w(vi为边的起点,vj为边的终点,w为边的权值),赋值map[vi][vj] = w,另外邻接矩阵的值和边的输⼊顺序⽆关。
对于邻接矩阵来说,初始化需要O(n^2)的时间,建图需要O(m),所以总时间复杂度是O(n^2),空间上,邻接矩阵的开销也是O(n^2),和点的个数有关。
⼆、前向星 前向星是⼀种通过存储边的⽅式来存储图的数据结构。
构造的时候,只需要读⼊每条边的信息,将边存放在数组中,把数组中的边按照起点顺序排序,前向星就构造完毕,为了查询⽅便,经常会有⼀个数组存储起点为vi的第⼀条边的位置. 由于涉及排序,前向星的构造时间复杂度与排序算法有关,⼀般情况下时间复杂度为O(mlogN),空间上需要两个数组,所以空间复杂度为O(m + n),有点在于可以应对点⾮常多的情况,可以存储重边,但是不能直接判断任意两个顶点之间是否有边.1 #include <iostream>2 #include <cmath>3 #include <cstdio>4 #include <cstring>5 #include <cstdlib>6 #include <algorithm>7using namespace std;8 typedef long long LL;910const int MAXN = 1000 + 3;11int head[MAXN]; //存储起点为Vi的边第⼀次出现的位置1213struct NODE14 {15int from;16int to;17int w;18 };19 NODE edge[MAXN];2021bool cmp(NODE a, NODE b)22 {23if(a.from == b.from && a.to == b.to) return a.w < b.w;24if(a.from == b.from) return a.to < b.to;25return a.from < b.from;26 }2728int main()29 {30 freopen("input.txt", "r", stdin);31int n,m;32 cin >> n >> m;33for(int i = 0; i < m; i++)34 {35 cin >> edge[i].from >> edge[i].to >> edge[i].w;36 }37 sort(edge, edge + m, cmp);38 memset(head, -1, sizeof(head));39 head[edge[0].from] = 0;40for(int i = 1; i < m; i++)41 {42if(edge[i].from != edge[i - 1].from)43 {44 head[edge[i].from] = i;45 }46 }47for(int i = 1; i <= n; i++)48 {49for(int k = head[i]; edge[k].from == i && k < m; k++)50 {51 cout << edge[k].from << '' << edge[k].to << '' << edge[k].w <<endl;52 }53 }54for(int i = 0; i <= n; i++)55 {56 cout << head[i] << "";57 }58 cout << endl;59return0;60 }三、链式前向星 链式前向星采⽤数组模拟链表的⽅式实现邻接表的功能,并且使⽤很少的额外空间,是当前建图和遍历效率最⾼的存储⽅式.数组模拟链表的主要⽅式是记录下⼀个节点的数组的在哪⼀个位置。
图的存储方法主要有邻接矩阵和邻接表两种。
1. 邻接矩阵:将图中的节点用一个二维数组来表示,如果节点i到节点j之间有一条边,则在数组中对应位上标识出来。
这是一个常用的存储方式,它可以快速地判断任意两个节
点之间是否有直接的连接关系。
但是当图中存在大量的无向边时(即所有的元素都不相
互连通)会造成内存浪费。
2. 链表法: 对于无向图而言, 我们可以使用单链表或者双向链表来保存诸如“v1->v2”
这样的信息, 其中 v1 和 v2 既代表了一条无向连通关系也代表了它们之间所包含的信
息(例如: 距离、时间、代价) , 这样就能够很好地避免内存浪费, 同时更加方便
快速地定位特定连通关系所包含的信息。
图的3种储存⽅式图的储存⽅式有三种⼀。
邻接矩阵 优点:简洁明了,调⽤⽅便,简单易写; 缺点:内存占⽤⼤,⽽且没办法存重边(可能可以,但我不会),点的个数超过 3000 直接爆炸 适⽤范围:点的个数少,稠密图,⼀般结合floyed使⽤,可以传递闭包。
代码:scanf("%d%d",&u,&v,&w);a[u][v]=w;a[v][u]=w;// 双向边⼆。
邻接表 优点:占⽤空间⼩,可以快速查找每个点的出度,重边可以存,写着较为⽅便 缺点:查找和删除边很不⽅便,对于⽆向图,如果需要删除⼀条边,就需要在两个链表上查找并删除,⽤了STL,速度会慢 适⽤范围:⼤部分情况,不要求删除边就⾏ 代码:struct Edge{int v,w;};vector <Edge> edge[maxn];void addedge(int u,int v,int w){edge[u].push_back({v,w});edge[v].push_back({u,w});//双向边}三。
链式前向星 优点:⽐邻接表还省空间,可以解决某些卡空间的问题,删除边也很⽅便,只需要更改next指针的指向即可,速度也快 缺点:好像就是写的⿇烦,理解⿇烦,性能好像很猛 适⽤:需要删除边的题⽬,速度时间都要求⾼的题⽬ 代码:struct Edge{int to,w,next;}edge[maxn*2];int cnt,head[maxn],s,t,n,m;void addedge(int u,int v,int w){edge[++cnt].to=v;edge[cnt].w=w;edge[cnt].next=head[u];head[u]=cnt;}struct Pre{int v,edge;}pre[maxn]; 解释:这是⽐较难理解的⼀种⽅式,所以做⼀下解释,主要是看别⼈的博客看懂的 对于上图,输⼊为 1 2 2 3 3 4 1 3 4 1 1 5 4 5 对于上⾯的结构体, 其中edge[i].to表⽰第i条边的终点 ,edge[i].next表⽰与第i条边同起点的下⼀条边的存储位置, edge[i].w为边权值. 数组head[],它是⽤来表⽰以i为起点的第⼀条边存储的位置, head[]数组⼀般初始化为-1 实际上你会发现这⾥的第⼀条边存储的位置其实在以i为起点的所有边的最后输⼊的那个编号. 有了以i为起点的第⼀条边的储存位置和同起点下⼀条边的储存位置我们就可以便利这个i点的每⼀条边了 初始化cnt = 0,这样,现在我们还是按照上⾯的图和输⼊来模拟⼀下: edge[0].to = 2; edge[0].next = -1; head[1] = 0; edge[1].to = 3; edge[1].next = -1; head[2] = 1; edge[2].to = 4; edge[2],next = -1; head[3] = 2; edge[3].to = 3; edge[3].next = 0; head[1] = 3; edge[4].to = 1; edge[4].next = -1; head[4] = 4; edge[5].to = 5; edge[5].next = 3; head[1] = 5; edge[6].to = 5; edge[6].next = 4; head[4] = 6; 很明显,head[i]保存的是以i为起点的所有边中编号最⼤的那个,⽽把这个当作顶点i的第⼀条起始边的位置. 这样在遍历时是倒着遍历的,也就是说与输⼊顺序是相反的,不过这样不影响结果的正确性. ⽐如以上图为例,以节点1为起点的边有3条,它们的编号分别是0,3,5 ⽽head[1] = 5 我们在遍历以u节点为起始位置的所有边的时候是这样的: for(int i=head[u];~i;i=edge[i].next) 那么就是说先遍历编号为5的边,也就是head[1],然后就是edge[5].next,也就是编号3的边,然后继续edge[3].next,也就是编号0的边,可以看出是逆序的.。
数据结构图的存储结构及基本操作数据结构图的存储结构及基本操作1·引言数据结构图是一种用来描述数据元素之间关系的图形结构,它可以表示实体之间的联系和依赖关系。
本文将介绍数据结构图的存储结构及基本操作。
2·存储结构2·1 邻接矩阵邻接矩阵是使用二维数组来表示数据结构图中各个节点之间的关系。
矩阵的行和列代表节点,如果两个节点之间存在边,则矩阵相应位置的值为1,否则为0。
2·2 邻接表邻接表是使用链表来表示数据结构图中各个节点之间的关系。
每个节点都有一个链表,链表中的每个元素表示与该节点相邻的节点。
2·3 十字链表十字链表是使用链表来表示数据结构图中各个节点之间的关系。
每个节点都有两个链表,一个表示该节点指向的节点,另一个表示指向该节点的节点。
2·4 邻接多重表邻接多重表是使用链表来表示数据结构图中各个节点之间的关系。
每个节点都有一个链表,链表中的每个元素表示与该节点相邻的边。
3·基本操作3·1 创建图创建一个空的数据结构图,根据需要选择适当的存储结构。
3·2 插入节点在数据结构图中插入一个节点,并建立与其他节点的关系。
3·3 删除节点从数据结构图中删除一个节点,并删除与其他节点的关系。
3·4 插入边在数据结构图中插入一条边,连接两个节点。
3·5 删除边从数据结构图中删除一条边,断开两个节点的连接。
3·6 遍历图按照某种规则遍历整个数据结构图,访问每个节点。
本文档涉及附件:无本文所涉及的法律名词及注释:1·邻接矩阵:用于表示图的存储结构,矩阵的行和列代表图的节点,矩阵的值表示节点之间的连接关系。
2·邻接表:用于表示图的存储结构,每个节点都有一个链表,链表中的每个元素表示与该节点相邻的节点。
3·十字链表:用于表示图的存储结构,每个节点都有两个链表,一个表示该节点指向的节点,另一个表示指向该节点的节点。
图的种类及储存⽅式⼀.图的种类(以下的分类不是并列的)1.有向图:图中边的⽅向是⼀定的,不能逆序⾛。
2.⽆向图:图中的边没有⽅向,可以逆序⾛。
没有正负⽅向3.完全图:完全图:对于顶中的每⼀个顶点,都与其他的点有边直接相连⽆向完全图:任意⼀个具有n个结点的⽆向简单图,其边数n*(n-1)/2;我们把边数恰好等于n*(n-1)/2的n个结点的称为完全图。
有向完全图:在⼀个n个结点的中,最⼤边数为n*(n-1)。
4.稀疏图和稠密图:⼀般的对于⼀个图来说,边的数⽬多的就是稠密图,边的数⽬少的就是稀疏图。
5.⼆部图与完全⼆部图(⼆部图也就是⼆分图)⼆分图的概念:简⽽⾔之,就是顶点集V可分割为两个互不相交的⼦集,并且图中每条边依附的两个顶点都分属于这两个互不相交的⼦集,两个⼦集内的顶点不相邻。
两个⼦集:A,B;性质满⾜:A∩B=∅,A∪B=V,这就是⼆分图。
6.图的⽣成树:把图中的n个点,和图中的n-1条边挑出来,如果这n-1条边能把这n个点连起来,那这就是图的⼀个⽣成树7.有向⽹,⽆向⽹:⽹就是加权的图8.活动⽹络:AOV图:顶点是活动,有向边表⽰活动之间的前驱后继关系--拓扑排序AOE图:E,也就是⽤边表⽰活动,⽤边表⽰的⽬的是利⽤边的权值,⽐如⽤边的权值表⽰最长时间--关键路径⼆:图的存储表⽰1. 邻接矩阵也就是⽤jz[i][j]表⽰i--j的连通情况,可以表⽰权值,也可以表⽰是否连通。
2.邻接表:就是把同⼀个顶点出发的边的链接储存在同⼀个边链表中,边链表的每⼀个结点代表⼀条边,称为边结点,边结点包括的信息可以⾃⼰确定。
⼀种⽅法:g[i][j]代表从i出发的第j条边的编号,对应着edge数组中的储存着边的信息,可以直接从g[i]得到从i出发的边的数⽬。
另⼀种就是边表了。
3.边表:就不介绍了,因为⼀直以来我都是⽤“邻接矩阵”和“边表”的,⽐较熟悉。
图的三种存储⽅式⼀、邻接矩阵适⽤:稠密图,就是说点数的平⽅与边数接近的情况,换句话说就是边特别多。
不适⽤:稀疏图,就是点数的平⽅与边数差的特别多,边数少,但点数多,就不⾏了,因为空间占⽤太⼤了。
实现代码#include <bits/stdc++.h>using namespace std;const int N = 1010; //图的最⼤点数量int n;int v[N][N]; //邻接矩阵/*** 测试数据40 5 2 35 0 0 12 0 0 43 14 0*/int main() {cin >> n;//读⼊到邻接矩阵for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)cin >> v[i][j];//下⾯的代码将找到与点i有直接连接的每⼀个点以及那条边的长度for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++)if (v[i][j]) cout << "edge from point "<< i << " to point " << j << " with length " << v[i][j] << endl;return 0;}⼆、邻接表#include <bits/stdc++.h>using namespace std;const int N = 1010; //图的最⼤点数量struct Edge { //记录边的终点,边权的结构体int to; //终点int value; //边权};int n, m; //表⽰图中有n个点,m条边vector<Edge> p[N]; //使⽤vector的邻接表/*** 测试数据4 62 1 11 3 24 1 42 4 64 2 33 4 5*/int main() {cin >> n >> m;//m条边for (int i = 1; i <= m; i++) {int u, v, l; //点u到点v有⼀条权值为l的边cin >> u >> v >> l;p[u].push_back({v, l});}//输出for (int i = 1; i <= n; i++) {printf("出发点:%d ", i);for (int j = 0; j < p[i].size(); j++)printf(" ⽬标点:%d,权值:%d;", p[i][j].to, p[i][j].value);puts("");}return 0;}三、链式前向星链式前向星是邻接表存图的第⼆种⽅法,它⾃⼰还有两种写法,⽐⽤向量存图的那种邻接表要快。
之前几天把数据结构扔在一边,在看离散数学的图论部分,看了大部分,最后还是觉得纯数学的,有一些可能现在我刚接触图还不会觉得有什么用,所以就选择性的跳过一些,现在也决定先放下书,回到数据结构上,开始图的部分的学习。
图的存储通用的存储方式有邻接矩阵表示法、邻接表表示法。
为方便有向图的顶点的入度与出度的计算,有有向图的十字链表表示法。
为方便对无向图的边进行操作,有无向图的邻接多重表表示法。
邻接矩阵表示法应该算是最容易的一种表示法,一些简单的操作比如查找某顶点的指定邻接点等很容易实现。
邻接表表示在计算无向图顶点的度很方便,计算有向图的出度也很方便,但是计算入度的话就要从第一个结点开始遍历,比较麻烦,这时采用逆邻接表表示法的话,求有向图的入度就会很方便,相应的,出度就不方便了,所以要根据需要选择存储结构。
如果在程序中要统计有向图的度,那么最好的方式就是采用十字链表的存储方式。
邻接多重表可以看作是对无向图的邻接矩阵的一种压缩表示,当然这种结构在边的操作上会方便很多,但是我现在还没学到,所以暂时还不知道。
下面是几种表示方法的算法实现,逆邻接表和邻接表的实现方式几乎一样,所以就不贴出来了。
1.#define MAX_VERTEX_NUM 202.3.#include<iostream>4.#include<string>ing namespace std;6.7.template<class T>8.int Locate_Vex(T G,string x) //定位顶点位置9.{10.for(int k=0;G.vexs[k]!=x;k++);11.return k;12.}13.14.//邻接矩阵存储图15.struct MGraph16.{17. string vexs[MAX_VERTEX_NUM];//顶点数组18.int arcs[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //邻接矩阵19.int vexnum;//顶点数目20.int arcnum;//边数目21.};22.23.void CreateUDN_MG(MGraph &G)24.{25.//采用邻接矩阵表示法,构造无向网26. cin>>G.vexnum>>G.arcnum;27.for(int i=0;i<G.vexnum;i++)28. cin>>vaxs[i];29.30.for(i=0;i<G.vexnum;i++)31.for(int j=0;j<G.vexnum;j++)32. G.arcs[i][j]=-1;33.//上面是初始化邻接矩阵,-1表示两点间边的权值为无穷大34.35.for(int k=0;k<G.arcnum;k++)36. {37. string v1,v2;38.int w;39. cin>>v1>>v2>>w;40. i=Locate_Vex(G,v1);41. j=Locate_Vex(G,v2);42.while(i<0|| i>G.vexnum-1 || j<0 || j>G.vexnum-1)43. {44. cout<<"结点位置输入错误,重新输入: ";45. cin>>v1>>v2>>w;46. i=Locate_Vex(G,v1);47. j=Locate_Vex(G,v2);48. }49. G.arcs[i][j]=w;50. G.arcs[j][i]=G.arcs[i][j]; //置对称边51. }52.}53.54.//邻接表存储图55.//表结点56.struct ArcNode57.{58.int adjvex; //弧所指向顶点的位置59. ArcNode *nextarc;// 指向下一条弧60.};61.62.//头结点63.typedef struct VNode64.{65. string data;//顶点名66. ArcNode *firstarc;//指向第一条关联顶点的弧67.}AdjList[MAX_VERTEX_NUM];68.69.struct ALGraph70.{71. AdjList vertices;//头结点数组72.int vexnum;73.int arcnum;74.};75.76.void CreateDG_ALG(ALGraph &G)77.{78.//采用邻接表存储表示,构造有向图G79. string v1,v2;80.int i,j,k;81. cin>>G.arcnum>>G.vexnum;82.83.//构造头结点数组84.for(i=0;i<G.vexnum;i++)85. {86. cin>>G.vertices[i].data;87. G.vertices[i].firstarc=NULL;88. }89.90.//输入各弧并构造邻接表91.for(k=0;k<G.arcnum;k++)92. {93. cin>>v1>>v2;94. i=Locate_Vex(G,v1);95. j=Locate_Vex(G,v2);96.while(i<0|| i>G.vexnum-1 || j<0 || j>G.vexnum-1)97. {98. cout<<"结点位置输入错误,重新输入: ";99. cin>>v1>>v2;100. i=Locate_Vex(G,v1);101. j=Locate_Vex(G,v2);102. }103.104. ArcNode *p=new ArcNode;105. p->adjvex=j;106. p->nextarc=NULL;107. p->nextarc=G.vertices[i].firstarc;108. G.vertices[i].firstarc=p;109. }110.}111.112.//十字链表方式存储有向图113.//弧结点114.struct ArcBox115.{116.int tailvex,headvex;//弧结点头尾结点位置117. ArcBox *hlink,*tlink;//弧头和弧尾相同的弧的链域118.};119.120.//顶点结点121.struct VexNode122.{123. string data;124. ArcBox *firstin,*firstout;//顶点第一条入弧和出弧125.};126.127.struct OLGraph128.{129. VexNode xlist[MAX_VERTEX_NUM];130.int vexnum;131.int arcnum;132.};133.134.void CreateDG_OLG(OLGraph &G)135.{136.//采用十字链表存储表示,构造有向图G137. string v1,v2;138.int i,j,k;139. cin>>G.vexnum>>G.arcnum;140.for(i=0;i<G.vexnum;i++)141. {142. cin>>G.xlist[i].data;143. G.xlist[i].firstin=NULL;144. G.xlist[i].firstout=NULL;145. }146.for(k=0;k<G.arcnum;k++)147. {148. cin>>v1>>v2;149. i=Locate_Vex(G,v1);150. j=Locate_Vex(G,v2);151.152.while(i<0|| i>G.vexnum-1 || j<0 || j>G.vexnum-1) 153. {154. cout<<"结点位置输入错误,重新输入: ";155. cin>>v1>>v2;156. i=Locate_Vex(G,v1);157. j=Locate_Vex(G,v2);158. }159.160. ArcBox *p=new ArcBox;161. p->tailvex=i;162. p->headvex=j;163. p->hlink=G.xlist[j].firstin;164. p->tlink=G.xlist[i].firstout;165. G.xlist[i].firstout=G.xlist[j].firstin=p;166. }167.}168.169.//邻接多重表存储170.//边结点171.struct EBox172.{173.int mark;//标志域,指示该边是否被访问过(0:没有 1:有) 174.int ivex,jvex;//该边关联的两个顶点的位置175. EBox *ilink,*jlink;//分别指向关联这两个顶点的下一条边176.};177.178.//顶点结点179.struct VexBox180.{181. string data;182. EBox *firstedge;//指向第一条关联该结点的边183.};184.185.struct AMLGraph186.{187. VexBox adjmulist[MAX_VERTEX_NUM];188.int vexnum;189.int arcnum;190.};191.192.void CreateUDG_AML(AMLGraph &G)193.{194.//用邻接多重表存储,构造无向图G195. string v1,v2;196.int i,j,k;197. cin>>G.vexnum>>G.arcnum;198.for(i=0;i<G.vexnum;i++)199. {200. cin>>G.adjmulist[i].data;201. G.adjmulist[i].firstedge=NULL;202. }203.204.for(k=0;k<G.arcnum;k++)205. {206. cin>>v1>>v2;207. i=Locate_Vex(G,v1);208. j=Locate_Vex(G,v2);209.210.while(i<0|| i>G.vexnum-1 || j<0 || j>G.vexnum-1) 211. {212. cout<<"结点位置输入错误,重新输入: ";213. cin>>v1>>v2;214. i=Locate_Vex(G,v1);215. j=Locate_Vex(G,v2);216. }217.218. EBox *p=new EBox;219. p->ivex=i;220. p->jvex=j;221. p->ilink=G.adjmulist[i].firstedge;222. p->jlink=G.adjmulist[j].firstedge;223. p->mark=0;224. G.adjmulist[i].firstedge=G.adjmulist[j].firstedge=p; 225. }226.}。
四种数据存储结构---顺序存储链接存储索引存储散列存储存储结构分四类:顺序存储、链接存储、索引存储和散列存储。
顺序结构和链接结构适⽤在内存结构中。
索引结构和散列结构适⽤在外存与内存交互结构。
顺序存储:在计算机中⽤⼀组地址连续的存储单元依次存储线性表的各个数据元素,称作线性表的顺序存储结构。
特点:1、随机存取表中元素。
2、插⼊和删除操作需要移动元素。
链接存储:在计算机中⽤⼀组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的)。
它不要求逻辑上相邻的元素在物理位置上也相邻.因此它没有顺序存储结构所具有的弱点,但也同时失去了顺序表可随机存取的优点。
特点:1、⽐顺序存储结构的存储密度⼩ (每个节点都由数据域和指针域组成,所以相同空间内假设全存满的话顺序⽐链式存储更多)。
2、逻辑上相邻的节点物理上不必相邻。
3、插⼊、删除灵活 (不必移动节点,只要改变节点中的指针)。
4、查找结点时链式存储要⽐顺序存储慢。
5、每个结点是由数据域和指针域组成。
索引存储:除建⽴存储结点信息外,还建⽴附加的索引表来标识结点的地址。
索引表由若⼲索引项组成。
特点:索引存储结构是⽤结点的索引号来确定结点存储地址,其优点是检索速度快,缺点是增加了附加的索引表,会占⽤较多的存储空间。
散列存储:散列存储,⼜称hash存储,是⼀种⼒图将数据元素的存储位置与关键码之间建⽴确定对应关系的查找技术。
散列法存储的基本思想是:由节点的关键码值决定节点的存储地址。
散列技术除了可以⽤于查找外,还可以⽤于存储。
特点:散列是数组存储⽅式的⼀种发展,相⽐数组,散列的数据访问速度要⾼于数组,因为可以依据存储数据的部分内容找到数据在数组中的存储位置,进⽽能够快速实现数据的访问,理想的散列访问速度是⾮常迅速的,⽽不像在数组中的遍历过程,采⽤存储数组中内容的部分元素作为映射函数的输⼊,映射函数的输出就是存储数据的位置,这样的访问速度就省去了遍历数组的实现,因此时间复杂度可以认为为O(1),⽽数组遍历的时间复杂度为O(n)。
第2讲图的存储结构——教学讲义本讲介绍4种较常用的存储表示法:①邻接矩阵表示法;②邻接表;③邻接多重表;④十字链表。
由于每种方法各有利弊,因此可以根据实际应用问题来选择合适的存储表示方法。
①邻接矩阵表示法图的邻接矩阵表示法(Adjacency Matrix)也称作数组表示法。
它采用两个数组来表示图:一个是用于存储顶点信息的一维数组,另一个是用于存储图中顶点之间关联关系的二维数组,这个关联关系数组被称为邻接矩阵。
若G是一具有n个顶点的无权图,G的邻接矩阵是具有如下性质的n×n矩阵A:上图所示G1和G2的邻接矩阵如下所示。
若图G是一个有n个顶点的网,则它的邻接矩阵是具有如下性质的n×n矩阵AA1=图G1,G2的邻接矩阵(a) G1是有向图(b) G2是无向图例如:下图就是一个有向网及其邻接矩阵的示例。
邻接矩阵表示法的C 语言描述如下:#define MAX_VERTEX_NUM 20 /*最多顶点个数*/#define INFINITY 32768 /*表示极大值,即∞*//* 图的种类:DG 表示有向图, DN 表示有向网, UDG 表示无向图, UDN 表示无向网 */typedef enum{DG, DN, UDG, UDN} GraphKind;typedef char VertexData; /*假设顶点数据为字符型*/ typedef struct ArcNode{AdjType adj; /* 对于无权图,用1或0表示是否相邻;对带权图,则为权值类型 */OtherInfo info; } ArcNode;typedef struct{VertexData vertex[MAX_VERTEX_NUM]; /*顶点向量*/ArcNode arcs [MAX_VERTEX_NUM][MAX_VERTEX_NUM]; /*邻接矩阵*/ int vexnum, arcnum; /*图的顶点数和弧数*/ GraphKind kind; /*图的种类标志*/ } AdjMatrix; /*(Adjacency Matrix Graph )*/邻接矩阵法的特点如下:● 存储空间: 对于无向图而言,它的邻接矩阵是对称矩阵(因为若(v i ,v j )∈E (G ),则(v j ,v i )∈E (G )),因此可以采用特殊矩阵的压缩存储法,即只存储其下三角即可,这样,一个具有n 个顶点的无向图G ,它的邻接矩阵需要n (n -1)/2个存储空间即可。
【数据结构和算法】57图的存储结构在计算机科学中,数据结构和算法是至关重要的基础知识。
而图作为一种常见的数据结构,其存储结构的选择对于图的操作效率和空间利用率有着重要的影响。
接下来,让我们一起深入探讨图的存储结构。
图是由顶点(Vertex)和边(Edge)组成的数据结构,它可以用来表示各种实际问题中的关系,比如社交网络中的人际关系、交通网络中的道路连接等。
为了有效地存储和操作图,人们提出了多种存储结构。
邻接矩阵(Adjacency Matrix)是一种常见的图存储结构。
对于一个具有 n 个顶点的图,我们可以用一个 n×n 的矩阵来表示。
如果顶点 i 和顶点 j 之间有边相连,那么矩阵中第 i 行第 j 列的元素值为 1(或者边的权值);如果没有边相连,则元素值为 0。
邻接矩阵的优点是直观、简单,判断两个顶点之间是否有边相连的操作非常高效,时间复杂度为O(1)。
但它的缺点也很明显,对于稀疏图(边的数量相对较少的图)来说,会浪费大量的存储空间。
邻接表(Adjacency List)是另一种常用的存储结构。
对于每个顶点,我们使用一个链表来存储与其相邻的顶点。
邻接表可以有效地节省存储空间,特别是对于稀疏图。
在进行遍历顶点的相邻顶点等操作时,时间复杂度也相对较低。
然而,在判断两个顶点之间是否有边相连时,时间复杂度相对较高。
十字链表(Orthogonal List)是一种结合了邻接表和逆邻接表的存储结构。
它不仅能够方便地找到一个顶点的出边,还能快速找到入边,适用于有向图的操作。
还有一种存储结构是邻接多重表,主要用于无向图的存储,能够有效地解决在无向图中删除边和插入边时的操作复杂性问题。
在实际应用中,选择哪种图的存储结构取决于具体的问题和需求。
如果图的规模较小,或者需要频繁判断顶点之间是否有边相连,邻接矩阵可能是一个不错的选择。
而对于大规模的稀疏图,邻接表通常能够提供更好的空间效率。
比如说,在一个社交网络中,如果我们想要快速判断两个人是否是好友(是否有边相连),并且用户数量不是特别巨大,那么邻接矩阵可能更合适。
作业 6
一、比较树的三种存储结构图的优点和缺点:1.双亲表示法;2.(左)孩子(右)
兄弟表示法;3.孩子单链表表示法。
二、试画出下列树的存储结构图:
1.双亲表示法;
2.(左)孩子(右)兄弟表示法;
3.孩子单链表表示法。
三、给定21个字符组成的文本(电文):
A A A
B B B A A A A B B B
C C A C C
D D E
试为字符 A、B、C、D、E 设计哈夫曼(Huffman)编码:
1.画出哈夫曼树;
2.分别列出 A、B、C、D、E的哈夫曼码;
3.分别计算哈夫曼树的路径长度PL和带权路径长度WPL。
四、给定22个字符组成的文本(电文):
A A A
B B B A A A A B B B
C C C C C
D D
E F
试为字符 A、B、C、D、E、F设计哈夫曼(Huffman)编码:
1.画出哈夫曼树;
2.分别列出 A、B、C、D、E、F 的哈夫曼码;
3.分别计算哈夫曼树的路径长度PL和带权路径长度WPL。