由于图的结构比较复杂,任意两个顶点之间都可能存在联系,因此无法以数据元素在存储区中的物理位置来表示元素之间的关系。
即图没有顺序映像的存储结构。
另一方面,多重链表表示图是一件自然的事。它是一种最简单的链式映像结构。
即以一个由一个数据域和多个指针域组成的结点表示图中的一个顶点。
其中,数据域存储该顶点的信息,指针域存储指向其邻接点的指针。
由于各个结点的度数各不相同,最大度数和最小度数可能相差很多。
若按度数最大的顶点设计结点结构,则会浪费很多存储单元。
反之,若按照自己的度数设计不同的结点结构,又会给操作带来麻烦。
因此,在实际应用中很少采用多重链表这种结构。
而应根据具体的图和需要进行的操作,设计恰当的结点结构和表结构。
=====================================================
一、数组表示法
//图的数组(邻接矩阵)存储表示
1 #define INFINITY INT_MAX //最大值 2 3 #define MAX_VERTEX_NUM 20 //最大顶点个数 4 5 typedef enum {DG, DN, UDG, UDN} Graphkind; //图的类型(有向图、有向网、无向图、无向网) 6 7 typedef struct ArcCell { 8 VRType adj; //表示顶点关系类型,对于无权图,用1或0表示相邻否;对带权图,则为权值类型; 9 InfoType *info; //该弧相关信息的指针; 10 }ArcCell, AdjMatrix[MAX_VERTEX_NUM],[MAX_VERTEX_NUM]; 11 12 typedef struct { 13 VertexType vexs[]; //顶点向量 14 AdjMatrix arcs; //邻接矩阵 15 int vexnum, arcnum; //图的当前顶点数和弧数 16 GraphKind kind; //图的种类标志 17 18 }MGraph;
创建图
1 Status CreateGraph(MGraph &G) { 2 //采用数组(邻接矩阵)表示法,构造图G 3 scanf(&G.kind); 4 switch(G.kind){ 5 case DG: return CreateDG(G); //有向图 6 case DN: return CreateDN(G); //有向网 7 case UDG: return CreateUDG(G); //无向图 8 case UDN: return CreateUDN(G); //无向网 9 default: return ERROR; 10 } 11 }//CreateGraph
1 Status CreateUDN(MGraph &G){ 2 //采用数组表示法,构造无向网G 3 scanf(&G.vexnum, &G.arcnum, &IncInfo); //IncInfo为0,则各弧不含其它信息 4 for(i = 0; i<G.vexnum; ++i) 5 scanf(&G.vexs[i]); //构造顶点向量 6 for(i = 0; i<G.vexnum; ++i) //初始化邻接矩阵 7 for(j = 0; j<G.vexnum; ++j) 8 G.arcs[i][j] = {INFINITY, NULL}; //{adj, info} 9 for(k=0; k<G.arcnum; ++k) //构造邻接矩阵 10 { 11 scanf(&v1, &v2, &w); //输入一条边依附的顶点及权值 12 i = LocateVex(); 13 j = LocateVex(); 14 G.arcs[i][j].adj = w; 15 if(IncInfo) Input(*G.arcs[i][j].info); 16 G.arcs[j][i] = G.arcs[i][j]; 17 } 18 return OK; 19 20 }//Create UDN
=====================================================
二、邻接表
图的邻接表:https://blog.csdn.net/qq_37043100/article/details/80243902
邻接表的出现是因为如果图是稀疏图的话,使用邻接矩阵表示法会造成空间的浪费。
毕竟要开辟一个一维数组和一个二维数组。
邻接表为了避免内存的浪费引入了链式存储。
1.用一个一维数组存储顶点,当然你也可以用单链表存储;
2.用单链表存储顶点的邻接点(从顶点出发的边),可以将顶点改为结构体数组,结构体中存放邻接点的指针,邻接点也创建一个结构体,定义指针next存放该顶点的另一个邻接点,这样就可以把该顶点的所有邻接点串起来了。
顶点表是个结构体数组,该结构体表示一个顶点,结构体中有data元素,存放顶点信息。 firstarc是一个边结构体表指针,存放邻接点的信息。
边表也是一个结构体,内有adjvex元素,存放邻接点的下标,info存放顶点与邻接点之间线(边)的权重,nextarc是边表结点指针,存放该顶点的下一个邻接点,nextarc负责将顶点的邻接点连起来。
边表应该理解成边的表,是从改顶点出发的边的表。每个边表中的结点存储了边所指向的顶点。同时nextarc指向下一个边表结点。
//图的邻接表的存储表示
1 #define MAX_VERTEX_NUM 20 2 typedef struct ArcNode { 3 int adjvex; //该弧指向的顶点的位置 4 struct ArcNode *nextarc; //指向下一条弧的指针 5 InfoType *info; //该弧相关信息的指针 6 }ArcNode;//边表结点 7 8 typedef struct VNode{ 9 Vertex data; //顶点信息 10 ArcNode *firstarc; //指向第一条依附该顶点的弧的指针 11 }VNode, AdjList[MAX_VERTEX_NUM]; 12 13 typedef struct { 14 AdjList vertices; 15 int vexnum, arcnum; //图的当前顶点数和弧数 16 int kind; //图的种类标志 17 }ALGraph;
=====================================================
十字链表及其C++实现:https://blog.csdn.net/qq_37366877/article/details/80288110
三、十字链表
十字链表是有向图的另一种链式存储结构。
可以看成是有向图的邻接表和逆邻接表结合起来得到的一种链表。
在十字链表中,对应于有向图中每条弧有一个结点,对应于每个顶点也有一个结点。
//有向图的十字链表存储表示
1 #define MAX_VERTEX_NUM 20 2 typedef struct ArcBox { 3 int tailvex, headvex; //该弧的尾和头顶点的位置 4 struct ArcBox *hlink, *tlink; //分别为弧头相同和弧尾相同的弧的链域 5 InfoType *info; //该弧相关信息的指针 6 }ArcBox; 7 8 typedef struct VexNode { 9 VertexType data; 10 ArcBox *firstin, *firstout; //分别指向该顶点的第一条入弧和出弧 11 }VexNode; 12 13 typedef struct { 14 VexNode xlist[MAX_VERTEX_NUM]; //表头向量 15 int vexnum, arcnum; //有向图的当前顶点数和弧数 16 17 }OLGraph;
只要输入n个顶点的信息和e条弧的信息,便可以建立该有向图的十字链表,其算法表示如下:
1 Status CreateDG(OLGraph &G) { 2 //采用十字链表存储表示,构造有向图G(G.kind = DG) 3 scanf(&G.vexnum, &G.arcnum, &IncInfo); 4 for(i = 0; i<G.vexnum; ++i) { //构造表头向量 5 scanf(&G.xlist[i].data); //输入顶点值 6 G.xlist[i].firstin = NULL; 7 G.xlist[i].firstout = NULL; //初始化指针 8 } 9 for(k=0; k<G.arcnum; ++k) { //输入各弧并构造十字链表 10 scanf(&v1, &v2); //输入一条弧的起点和终点 11 i = LocateVex(G, v1); j = LocateVex(G, v2); //确定v1和v2在G中的位置 12 p = (ArcBox *)malloc(sizeof(ArcBox)); //为弧结点分配空间 13 *p = {i, j, G.xlist[j].firstin, G.xlist[i].firstout, NULL} //对弧结点赋值 14 G.xlist[j].firstin = G.xlist[i].firstout = p; //完成在入弧和出弧链头的插入 15 if(IncInfo) Input(*p ->info); //若弧含有相关信息,则输入 16 } 17 }//CreateDG
在十字链表中很容易找到Vi为尾的弧,也容易找到以Vi为头的弧,因而容易求得顶点的出度和入度。
在某些有向图的应用中,十字链表是很有用的工具。
=====================================================
邻接多重表:https://www.cnblogs.com/Trojan00/p/8964609.html
四、邻接多重表
邻接多重表示无向图的另一种链式存储结构。
顶点表结点由两个域组成,vertex 域存储和该顶点相关的信息;firstedge 域指示第一条依附于该顶点的边;
边表结点由六个域组成,mark 为标记域,可用以标记该条边是否被搜索过;ivex 和jvex 为该边依附的两个顶点在图中的位置;ilink 指向下一条依
附于顶点ivex的边;jlink 指向下一条依附于顶点jvex 的边,info 为指向和边相关的各种信息的指针域。
//无向图的邻接多重表存储表示
1 #define MAX_VERTEX_NUM 20 2 typedef enum {unvisited, visited} VisitIf; 3 4 typedef struct EBox { 5 VisitIf mark; //是否访问过 6 int ivex, jvex; //该边依附的两个顶点的位置 7 struct EBox *ilink *jlink; //分别指向依附这两个顶点的下一条边 8 InfoType *info; //该边信息指针 9 }EBox; 10 11 typedef struct VexBox { 12 VertexType data; 13 EBox *firstedge; //指向第一条依附于该顶点的边 14 }VexBox; 15 16 typedef struct { 17 VexBox adjmulist[MAX_VERTEX_NUM]; 18 int vexnum, edgenum; //无向图的当前顶点数和边数 19 }AMLGraph;
虽然邻接表是无向图的一种很好表示方法,但是邻接表中每一条边有两个顶点,分别在第i,第j个顶点链表中。这给某些图的操作带来不便。
例如在某些图的应用问题中需要对边进行某些操作,如对已被搜索过的边作记号或删除一条边等,一次需要找到表示同一条边的两个顶点。
在进行这类操作时,使用邻接多重表作存储结构更加适宜。