数据结构之图的存储结构

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangquan2015/article/details/82840120

图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为G(V,E),其中,G表示一个图,V是顶点集合,E是边集合

邻接矩阵

∞是一个极限值,用65535表示,权值大多数情况是正值,个别时候是0,甚至有时候是负值,所以用∞表示不存在。

在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS//避免scanf不安全报错

#include "stdio.h"    
#include "stdlib.h"   
#include "io.h"  
#include "math.h"  
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXVEX 100 /* 最大顶点数,应由用户定义 */
#define INFINITY 65535
typedef int Status;	/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef char VertexType; /* 顶点类型应由用户定义  */
typedef int EdgeType; /* 边上的权值类型应由用户定义 */
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] = 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]; /* 因为是无向图,矩阵对称 */
	}
}
int main(void)
{
	MGraph G;
	CreateMGraph(&G);
	system("pause");
	return 0;
}

邻接表

邻接矩阵是不错的一种图存储结构,但是我们也发现,对于边数相对顶点较少的图,这种结构是存在对存储空间的极大浪费的。可以将数组与链表相结合,称为邻接表。
在这里插入图片描述
此时我们很容易就算出某个顶点的入度出度是多少,判断两顶点是否存在弧也很容易实现。
对于带权值的网图,可以在边表结点定义中再增加一个weight的数据域,存储权值信息即可。
在这里插入图片描述

#define _CRT_SECURE_NO_WARNINGS//避免scanf不安全报错

#include "stdio.h"    
#include "stdlib.h"   
#include "io.h"  
#include "math.h"  
#include "time.h"
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXVEX 100 /* 最大顶点数,应由用户定义 */
typedef int Status;	/* Status是函数的类型,其值是函数结果状态代码,如OK等 */
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 */
	}
}
int main(void)
{
	GraphAdjList G;
	CreateALGraph(&G);
	system("pause");
	return 0;
}

十字链表

对于有向图来说,邻接表是有缺陷的。关心了出度问题,想了解入度就必须要遍历整个图才能知道,反之,逆邻接表解决了入度却不了解出度的情况。可以将邻接表和逆邻接表结合,即十字链表,对于有向图来说,十字链表是非常好的数据结构模型。
重新定义顶点的表结构:
在这里插入图片描述
重新定义边的表结构:
在这里插入图片描述
在这里插入图片描述

邻接多重表

对于无向图的邻接表,关注的重点是顶点,那么邻接表是不错的选择,但如果我们更关注边的操作,比如对已访问的边做标记,删除某一条边等操作,那就意味着,需要找到这条边表结点进行操作,这其实是很麻烦的。因此,我们也仿照十字链表的方式,对边表结点的结构进行一些改造,也许就可以避免刚才提到的问题:

顶点1 边1 顶点2 边2
ivex ilink jvex jlink

在这里插入图片描述
ilink指向依附顶点ivex的下一条边,jlink指向依附顶点jvex的下一条边。
在这里插入图片描述
这样对边的操作就方便多了,若要删除左图的(v0,v2)这条边,只需要将右图的⑥⑨的链表指向改为^即可。

猜你喜欢

转载自blog.csdn.net/zhangquan2015/article/details/82840120