图的五种存储结构:
1、图的邻接矩阵表示法
图是由顶点和边或弧两部分组成。图的邻接矩阵(Adjacency Matrix)存储方式是用两个数组表示图,一个一维数组存储图中的顶点信息,一个二维数组(邻接矩阵)存储图中边或弧的信息。
2、图的邻接表表示法
邻接矩阵是一种不错的图存储结构,但我们发现对于边数相对顶点较少的图,这种结构是存在对存储空间的极大浪费的。因此采用邻接表(Adjacency List)存储图结构,就是将数组和链表相结合的存储方法,数组存储图的顶点信息,图中每个顶点的所有邻接点用链表存储,链表的头结点就是该顶点。
3、图的十字链表表示法(有向图的邻接表优化存储结构)
对于有向图,邻接表是有缺陷的,可以通过邻接表很简单计算出度,但想了解入度就必须要遍历整个图才知道,反之,逆邻接表解决入度却不了解出度的情况。十字链表就是将邻接表和逆邻接表结合起来的一种图的存储结构。顶点表结点结构:data(数据域存储顶点信息)、fistin(指针域指向入度的第一个结点)、firstout(指针域指向出度的第一个结点);边表结点结构:tailvex(边的尾部起点结点的下标)、headvex(边的头部终点结点的下标)、headlink(指针域指向头部终点结点相同的下一条边)、taillink(指针域指向尾部起点结点相同的下一条边)、weight(边上的权值);
4、邻接多重边(无向图的邻接表优化存储结构)
对于无向图邻接表表示法只关注顶点操作(如计算顶点的度),如果我们关注边的操作,比如对已访问的边做标记或者删除一条边等操作,那就意味着需要找到这条边的两个边表结点进行操作,比较麻烦。因此提出邻接多重边的存储结构优化无向图的操作,它和无向图的十字链表结构相似,顶边表结点的结构:ivex、ilink、jvex、jlink。ivex和jvex是某条边依附的两个顶点的下标,指针域ilink指向依附顶点ivex的下一条边,指针域jlink指向依附顶点jvex的下一条边。
5、边集数组(关注边的操作,克鲁斯卡尔Kruskal算法中用到)
边集数组是由两个一维数组构成。一个是存储顶点信息;另一个是存储边的信息,这个边数组每个元素由一条边的起点下标begin、终点下标end、权值weight组成。显然边集数组关注的是边的集合,在边集数组中查找一个顶点的度需要扫描整个边数组,效率并不高。因此它更适合对边依次进行处理操作,而不适合对相关顶点的操作。
6、图的存储结构选择策略
图的五种存储结构中最重要的是邻接矩阵和邻接表,它们分别代表着边集是用数组还是链表的方式存储。十字链表是针对有向图邻接表结构的优化,邻接多重表是针对无向图邻接表结构的优化。边集数组更多考虑对边的关注,如求最小生成树Kruskal算法它的思想就是对边集基于并查集的贪心算法。用什么存储结构需要具体问题具体分析,通过稠密图或者读取数据较多,结构修改较少的图,用邻接矩阵更合适,反之则应该考虑邻接表。
#include<iostream>
using namespace std;
#define MaxVex 100 //定义最大顶点数
#define Inf 65535 //定义无穷大
typedef char VertexType; //定义顶点数据类型
typedef int EdgeType; //定义边上权值数据类型
//邻接矩阵存储图的结构
typedef struct Graph
{
VertexType vex[MaxVex]; //一维数组存储顶点信息
EdgeType arc[MaxVex][MaxVex]; //二维数组存储边的权值信息,也就是邻接矩阵
int numVex,numArc; //记录图中当前顶点数、边数
}MatrixGraph;
//建立无向网图的邻接矩阵表示
void CreateMatrixGraph(MatrixGraph *G)
{
int i,j,k,weight;
//输入顶点数和边数
cin>>G->numVex>>G->numArc;
//输入顶点信息,建立顶点表
for(i=0;i<G->numVex;i++)
cin>>G->vex[i];
//初始化邻接矩阵
for(i=0;i<G->numVex;i++)
for(j=0;j<G->numVex;j++)
G->arc[i][j]=Inf;
//输入边信息,建立邻接矩阵
for(k=0;k<G->numArc;k++)
{
cin>>i>>j>>weight;
G->arc[i][j]=weight;
G->arc[j][i]=G->arc[i][j]; //无向图,矩阵对称
}
}
/*
*
*
*
*
*/
//邻接表存储图的结构
//边结点
typedef struct EdgeNode
{
int vex; //邻接点域,存储边另一个连接顶点对应的下标
EdgeType weight; //边的权值
struct EdgeNode *next; //指向下一个邻接点
}EdgeNode;
//顶点结点
typedef struct VexNode
{
VertexType data; //顶点域,存储顶点信息
EdgeNode *firstEdge; //边的头指针
}VexNode;
//邻接表存储所有顶点结点
typedef struct
{
VexNode adjList[MaxVex]; //adjList数组存储顶点结点
int numVex,numArc; //记录图中当前顶点数、边数
}AdjListGraph;
//建立无向图的邻接表结构
void CreateAdjListGraph(AdjListGraph *G)
{
int i,j,k,w;
EdgeNode *e;
//输入顶点数和边数
cin>>G->numVex>>G->numArc;
//输入顶点信息,建立顶点表
for(i=0;i<G->numVex;i++)
{
cin>>G->adjList[i].data; //输入顶点信息
G->adjList[i].firstEdge=NULL; //将边表置为空表
}
//输入边信息,建立边表
for(k=0;k<G->numArc;k++)
{
cin>>i>>j>>w;
e=(EdgeNode*)malloc(sizeof(EdgeNode));
e->vex=j;
e->weight=w;
//头插法链表插入边信息
e->next=G->adjList[i].firstEdge;
G->adjList[i].firstEdge=e;
//无向图,另一个顶点同样的操作
e=(EdgeNode*)malloc(sizeof(EdgeNode));
e->vex=i;
e->weight=w;
//头插法链表插入边信息
e->next=G->adjList[j].firstEdge;
G->adjList[j].firstEdge=e;
}
}
int main()
{
MatrixGraph G1;
AdjListGraph G2;
CreateMatrixGraph(&G1);
CreateAdjListGraph(&G2);
cout<<G1.numVex<<G2.numVex<<endl;
return 0;
}