数据结构之图(二)

tips:本文只涉及图的存储结构

图的存储结构

1.邻接矩阵

1.1 定义

所谓邻接矩阵存储,是指用一个一位数组存储图中顶点的信息,用一个二位数组存储图中边的信息,存储顶点之间邻接关系的二维数组称为邻接矩阵。
结点数为n的图 G = ( V , E ) G=(V,E) G=(V,E)的邻接矩阵A是 n ∗ n n*n nn的。G的顶点编号为 v 1 , v 2 , . . . , v n v_1,v_2,...,v_n v1,v2,...,vn。若 ( v i , v j ) ∈ E (v_i,v_j)\isin E (vi,vj)E,则 A [ i ] [ j ] = 0 A[i][j]=0 A[i][j]=0
A [ i ] [ j ] = { 1 , 若 ( v i , v j ) 或 < v i , v j > 是 E ( G ) 中的边 0 ,若 ( v i , v j ) 或 < v i , v j > 不是 E ( G ) 中的边 A[i][j] = \begin{cases} 1, 若(v_i,v_j)或<v_i,v_j>是E(G)中的边\\ 0,若(v_i,v_j)或<v_i,v_j>不是E(G)中的边 \end{cases} A[i][j]={1,(vi,vj)<vi,vj>E(G)中的边0,若(vi,vj)<vi,vj>不是E(G)中的边

无权值的无向图
无权值有向图
对于带权图而言,若顶点 v i v_i vi v j v_j vj之间有边相连,则邻接矩阵中对应项存放着该边对应的权值,若顶点 v i v_i vi v j v_j vj不相连,则用 ∞ \infin 来代表这两个顶点之间不存在边。
A [ i ] [ j ] = { w i j , 若 ( v i , v j ) 或 < v i , v j > 是 E ( G ) 中的边 0 或 ∞ ,若 ( v i , v j ) 或 < v i , v j > 不是 E ( G ) 中的边 A[i][j] = \begin{cases} w_{ij}, 若(v_i,v_j)或<v_i,v_j>是E(G)中的边\\ 0或\infin,若(v_i,v_j)或<v_i,v_j>不是E(G)中的边 \end{cases} A[i][j]={wij,(vi,vj)<vi,vj>E(G)中的边0,若(vi,vj)<vi,vj>不是E(G)中的边
带权值的无向图

带权值的有向图

1.2 实现代码

#define _CRT_SECURE_NO_WARNINGS 1


//图的存储结构:邻接矩阵(带权无向图)
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXVEX 100  //顶点数组的存储空间大小
#define GRAPH_INFINITY 65535   

typedef int Status;
typedef int VertexType;  //顶点的数据类型
typedef int EdgeType;  // 带权图中 边上权值的数据类型

typedef struct
{
	VertexType vexs[MAXVEX];  //顶点表   //从vexs[1]开始存储
	EdgeType arc[MAXVEX][MAXVEX];   //边表  //arc[0][]、arc[][0]不存储信息
	int numNodes;  //顶点数
	int numEdges;	//边数
}MGraph;

//创建图
void CreateMGraph(MGraph* G)
{
	int i, j, k, w;
	printf("输入顶点数和边数:\n");
	scanf("%d %d", &G->numNodes, &G->numEdges);

	//读入顶点信息, 建立顶点表
	for (i = 1; i <= G->numNodes; i++)
	{
		printf("请输入顶点信息:\n");
		scanf("%d", & G->vexs[i]);
	}

	/* 邻接矩阵初始化 */
	for (i = 1; i <= G->numNodes; i++)
	{
		for (j = 1; j <= G->numNodes; j++)
		{
			G->arc[i][j] = GRAPH_INFINITY; //初始化为 65535
		}
	}

	/* 读入numEdges条边,建立邻接矩阵 */
	for (k = 1; k <= G->numEdges; k++)
	{
		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()
{

	MGraph G;
	CreateMGraph(&G);


	//打印顶点表信息
	for (int i = 1; i <= G.numNodes; i++)
	{
		printf("%d ", G.vexs[i]);
	}
	printf("\n");

	//打印边表信息
	int i, j;
	for (i = 1; i <= G.numNodes; i++)
	{
		for (j = 1; j <= G.numNodes; j++)
		{
			printf("%d\t", G.arc[i][j]);
		}
		printf("\n");
	}


	return 0;
}

在这里插入图片描述

在这里插入图片描述

2. 邻接表

2.1 定义

当一个图为稀疏图时,使用邻接矩阵法显然要浪费大量的存储空间,而图的邻接表法结合了顺序存储和链式存储的方法,大大减少了这种不必要的浪费。
所谓邻接表,是指对图G中的每个顶点 v i v_i vi建立一个单链表,第i个单链表中的结点表示依附于顶点 v i v_i vi的边(对于有向图则是以顶点 v i v_i vi为尾的弧),这个单链表就称为顶点 v i v_i vi的边表(对于有向图则为出边表)。
边表的头指针和顶点的数据信息采用顺序存储,所以在邻接表中存在两种结点:顶点表结点和边表结点。
在这里插入图片描述

顶点表结点有顶点域(data)和指向第一条邻接边的指针(firstedge)构成,边表结点由邻接点域(adjvex)和指向下一条邻接边的指针域(nextarc)构成。
无向邻接表
有向邻接表
对于带权值的网图,可以再边表结点定义中再增加一个weight的数据域,存储权值信息。
带权值有向图

2.2 实现代码

#define _CRT_SECURE_NO_WARNINGS 1

#include <stdio.h>
#include <stdlib.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;
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;
	int numEdges;
}GraphAdjList;


//建立图的邻接表结构
void CreateALGraph(GraphAdjList* G)
{
	int i, j, k;
	EdgeNode* e;
	printf("输入顶点数和边数:\n");
	scanf("%d %d", &G->numNodes, &G->numEdges);

	//scanf("请按顺序输入顶点信息:\n");
	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);
		e = (EdgeNode*)malloc(sizeof(EdgeNode));
		e->adjvex = j;
		e->next = G->adjList[i].firstedge;
		G->adjList[i].firstedge = e;

		e = (EdgeNode*)malloc(sizeof(EdgeNode));
		e->adjvex = i;
		e->next = G->adjList[j].firstedge;
		G->adjList[j].firstedge = e;
	}
}

int main()
{
	GraphAdjList G;
	CreateALGraph(&G);

	return 0;
}

3. 十字链表

3.1 定义

将邻接表和逆邻接表结合起来便是:十字链表。
逆邻接表一般指有向图的逆邻接表,即对每个顶点 v i v_i vi都建立一个链接为 v i v_i vi为弧头的表。
tips:十字链表是有向图的一种存储方法。
其结构如下:

  1. 顶点表结点结构
    在这里插入图片描述
    其中,firstin表示入边表头指针,指向该顶点的入边表中第一个结点;firstout表示出边表头指针,指向该顶点的出边表中的第一个结点。

  2. 边表结点结构
    在这里插入图片描述

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

4. 邻接多重表

4.1 定义

对无向邻接表的优化,同一条边在邻接表中用两个结点表示,而在邻接多重表中只有一个结点,从而提高对边的操作的便利性。

  1. 顶点表结点结构(和邻接表相同)
    在这里插入图片描述

其中,data存储顶点信息,firstedge指向当前结点的第一条邻接边.

  1. 边结点结构
    在这里插入图片描述

    ivex和jvex是与某条边依附的两个顶点在顶点表中的下标;ilink指向依附顶点ivex的下一条边;jlink指向依附顶点jvex的下一条边。

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值