0
点赞
收藏
分享

微信扫一扫

图的存储结构

1 邻接矩阵

1.1 有向图和无向图

  图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息。设图中有n个顶点,则邻接矩阵是一个的方阵,定义为:

无向图实例:

图的存储结构_邻接表_03

设置两个数组,顶点数组,边数组表示的边存在,表示的边不存在。

有向图实例:

图的存储结构_邻接表_12

设置两个数组,顶点数组,边数组表示有弧,表示没有弧。

在有向图中有入度和出度的概念,顶点的入度为1,正是第列各数的和。顶点的出度为2,即第行各数之和。

1.2 网图

每条边上带有权的图叫做网,假设该图有n个顶点,则邻接矩阵是一个的方阵,定义为:

这里的表示$ (v_i,v_j) <v_i,v_j>$上的权值,根据此定义,我们可作出网的邻接矩阵。

图的存储结构_邻接表_29

1.3 邻接矩阵的代码实现

图的邻接矩阵存储结构如下:

typedef char VertexType;  // 顶点类型
typedef int EdgeType; // 边上的权值类型
#define MAXVEX 100 // 最大顶点数
#define INFINITY 65535 // 无穷用65535代替
typedef struct{
VertexType vexs[MAXVEX]; // 顶点表
EdgeType arc[MAXVEX][MAXVEX]; // 邻接矩阵,边表
int numNodes,numEdges; // 图中当前的顶点数和边数
}MGraph;

无向网图的创建:

/* 建立无向网图的邻接矩阵表示 */
void CreateMGraph(MGraph *G)
{
int i,j,k,w;
printf("输入顶点数和边数:\n");
scanf("%d,%d",&G->numNodes,&G->numEdges); /* 输入顶点数和边数 */
for(i = 0;i <G->numNodes;i++) /* 读入顶点信息,建立顶点表 */
scanf(&G->vexs[i]);
for(i = 0;i <G->numNodes;i++)
for(j = 0;j <G->numNodes;j++)
G->arc[i][j]=GRAPH_INFINITY; /* 邻接矩阵初始化 */
for(k = 0;k <G->numEdges;k++) /* 读入numEdges条边,建立邻接矩阵 */
{
printf("输入边(vi,vj)上的下标i,下标j和权w:\n");
scanf("%d,%d,%d",&i,&j,&w); /* 输入边(vi,vj)上的权w */
G->arc[i][j]=w;
G->arc[j][i]= G->arc[i][j]; /* 因为是无向图,矩阵对称 */
}
}

2 邻接表

2.1 邻接表存储结构

邻接矩阵对于边数相对顶点较少的图是存在对存储空间的极大浪费。我们可以对边或弧使用链式存储的方式来避免空间浪费的问题。将数组与链表相结合的存储方法称作邻接表。

邻接表的处理方法:

  1. 图中顶点用一个一维数组存储。对于顶点数组中,每个数据元素还需要存储指向第一个邻接表的指针,以便于查找该顶点的边信息。
  2. 图中每个顶点的所有邻接点构成一个线性表,由于邻接点的个数不定,所以用单链表存储,无向图称为顶点的边表,有向图则称为顶点作为弧尾的出边表。

图的存储结构_邻接表_33

对于带权值的网图,可以再边表结点定义中再增加一个​​weight​​的数据域,存储权值信息,如图所示。

图的存储结构_邻接表_34

2.2 邻接表代码实现

图的邻接表存储结构如下:

typedef char VertexType;  // 顶点类型
typedef int EdgeType; // 边上的权值类型

typedef struct EdgeNode /* 边表结点 */
{
int adjvex; /* 邻接点域,存储该顶点对应的下标 */
EdgeType info; /* 用于存储权值,对于非网图可以不需要 */
struct EdgeNode *next; /* 链域,指向下一个邻接点 */
}EdgeNode;

typedef struct VertexNode /* 顶点表结点 */
{
VertexType data; /* 顶点域,存储顶点信息 */
EdgeNode *firstedge;/* 边表头指针 */
}VertexNode, AdjList[MAXVEX];

typedef struct
{
AdjList adjList;
int numNodes,numEdges; /* 图中当前顶点数和边数 */
}GraphAdjList;

无向图的邻接表创建代码:

/* 建立图的邻接表结构 */
void CreateALGraph(GraphAdjList *G)
{
int i,j,k;
EdgeNode *e;
printf("输入顶点数和边数:\n");
scanf("%d,%d",&G->numNodes,&G->numEdges); /* 输入顶点数和边数 */
for(i = 0;i < G->numNodes;i++) /* 读入顶点信息,建立顶点表 */
{
scanf(&G->adjList[i].data); /* 输入顶点信息 */
G->adjList[i].firstedge=NULL; /* 将边表置为空表 */
}


for(k = 0;k < G->numEdges;k++)/* 建立边表 */
{
printf("输入边(vi,vj)上的顶点序号:\n");
scanf("%d,%d",&i,&j); /* 输入边(vi,vj)上的顶点序号 */
e=(EdgeNode *)malloc(sizeof(EdgeNode)); /* 向内存申请空间,生成边表结点 */
e->adjvex=j; /* 邻接序号为j */
e->next=G->adjList[i].firstedge; /* 将e的指针指向当前顶点上指向的结点 */
G->adjList[i].firstedge=e; /* 将当前顶点的指针指向e */

e=(EdgeNode *)malloc(sizeof(EdgeNode)); /* 向内存申请空间,生成边表结点 */
e->adjvex=i; /* 邻接序号为i */
e->next=G->adjList[j].firstedge; /* 将e的指针指向当前顶点上指向的结点 */
G->adjList[j].firstedge=e; /* 将当前顶点的指针指向e */
}
}

3 十字链表

将邻接表与逆邻接表结合起来叫做十字链表,用于解决邻接表出度和入度的问题。

重新定义顶点表顶点结构如图所示:

图的存储结构_结点_35

firstin表示入边表头指针,指向该顶点的出边表中的第一个结点;firstout表示出边表头指针,指向该顶点的出边表中的第一个结点。

重新定义的边表结点结构如图所示:

图的存储结构_结点_36

tailvex是指弧起点在顶点表中的下标;headvex是指弧终点在顶点表中的下标;headlink是指入边表指针域,指向终点相同的下—条边;taillink是指边表指针域,指向起点相同的下一条边。如果是网还可以再增加—个weight域来存储权值。 、

实例如下:

图的存储结构_邻接表_37

十字链表的好处就是因为把邻接表和逆邻接表整合在了一起,这样既容易找到以为尾的弧,也容易找到以为头的弧,因而容易求得顶点的出度和入度。而且它除了结构复杂一点外,其实创建图算法的时间复杂度是和邻接表相同的。因此,在有向图的应用中,十字链表是非常好的数据结构模型。

举报

相关推荐

0 条评论