数据结构-图(C语言)

本文介绍了图的基本概念,包括有向图、无向图和网,以及顶点的度和连通性的概念。重点讨论了图的两种存储结构——邻接矩阵和邻接表,提供了创建这两种存储结构的C语言实现。邻接矩阵通过二维数组表示图中顶点间的关系,邻接表则采用链式结构存储每个顶点的邻接边。文章还给出了创建邻接矩阵和邻接表的详细步骤和代码示例。
摘要由CSDN通过智能技术生成

图的基本概念

定义: 图(Graph)是一种非线性数据结构,形式化描述为:
Graph = (V, R)
其中,
V={Vi | Vi属于datatype, i = 0,1,2,…n-1}是图中元素Vi(称为顶点,Vertex)的集合,
n=0时,V为空集。
R={<Vi, Vj> | Vi, Vj属于V, 且p(Vi, Vj)存在}是图中顶点之间的关系集,P(Vi, Vj)
为顶点Vi到Vj之间是否存在路径的判定条件,即若Vi到Vj之间的路径存在,则关系<Vi, Vj>
属于R.

分类:
有向图 弧

无向图 边

网 :
若在图的关系<Vi, Vj>或<Vj, Vi>附加一个权值w
w
Vi ------> Vj
w称为弧或边上的权。带权的图称为网。权w的具体含义 图在不同的应用领域来确定。
如:顶点表示城市,权w可以视为两个城市之间距离等等。

顶点的度:
顶点边或弧的条数。

连通图 和 非连通图

路径:
从某个顶点到其他顶点是否存在路径

图的存储结构

“数组表示法”
“邻接表”

“十字链表”
“邻接多重表”

数组表示法 又称为 “邻接矩阵”
G = (V, R)
可以用两个数组来存储图G:
一个一维数组存储图G中的各个顶点。
V 用另外一个二维数组A来存储图G中顶点之间的关系R.
第i行,表示 以顶点V[i]为起点
第j列, 表示 以顶点V[j]为终点

图中的数据类型:

图中顶点类型: 
				typedef char Vtype;
			权值的类型 : 
				typedef int Adjtype;
				
			//图中顶点的最大数目  
				#define MAXN 100 
				
			图的类型: 
				typedef struct graph 
				{
					Vtype V[MAXN]; //一维数组来存储顶点
					
					Adjtype A[MAXN][MAXN]; //二维数组来存储顶点与顶点之间的关系
											"邻接矩阵"
											
					int vexnum; //图中有效的顶点个数 
					int arcnum; //图中有效边的条数 
					//......
				
				}Graph;

创建邻接矩阵
需要写一个查找顶点在一维数组中的下标

//找到某一个顶点对应的下标
int Find_V_index(Graph* g, Vtype vex)
{
	int i;
	for (i = 0; i < g->vexnum; i++)
	{
		if (g->V[i] == vex)
		{
			//找到了
			return i;
		}
	}

	return -1;//表示没有找到
}

Graph* Create_Graph(void)
{
	Graph* g = malloc(sizeof(Graph));
	g->arcnum = 0;
	g->vexnum = 0;
	Vtype d;
	while (1)
	{
		//约定当用户输入为‘#’表示结束 
		scanf("%c", &d);
		if (d == '#')
		{
			break;
		}
		g->V[g->vexnum] = d;
		g->vexnum++;
	}

	//过滤掉'\n'
	getchar();
	
	//将二维数组中,有效区域数值全部初始化为无穷大 ‘
	int i, j;
	for (i = 0; i < g->vexnum; i++)
	{
		for (j = 0; j < g->vexnum; j++)
		{
			g->A[i][j] = Very_Big;
		}
	}
	Vtype s, e;
	int s_i, e_i; //s和e分别对应的下标
	Adjtype w;
	while (1)
	{
		scanf("%c%c%d", &s, &e, &w);
		if (s == '#' && e == '#' && w == 0)
		{
			break;
		}

		//先找到s对应在邻接矩阵中下标
		s_i = Find_V_index(g, s);

		//再找到e对应在邻接矩阵中下标 
		e_i = Find_V_index(g, e);


		g->A[s_i][e_i] = w;

		//过滤掉'\n'
		getchar();
	}

	return g;
}

邻接表
链式结构来存储图
所谓邻接表,是将图中每一个顶点v和由v发出的弧
或边构成单链表。邻接表是图的一种链式存储结构。

数据类型:

			typedef char Vtype; //顶点的类型 
			typedef int Adjtype; //权值的类型 
			
			typedef struct bian 
			{
				int stop_index; //终点的下标
				Adjtype w; //权值 
				struct bian * next; //下一条边的指针
			}Bian;
			
			//顶点元素的结构体 
			typedef struct vertex 
			{
				Vtype data; //顶点的数据域
				
				Bian * first; //指向由该顶点为起点发出的第一条边
				
			}Vertex;
			
			//图中顶点元素的最大数目 
			#define MAXN 100
			
			//图的类型 
			Vertex Graph[MAXN];

创建邻接表
与创建邻接矩阵思路一样,只是插入数据时用的是链表的思想

//找起点元素下标
static int Find_index(Graph* G, Vtype s)
{
	int i;
	for (i = 0; i < G->vexn; i++)
	{
		if (G->g[i].data == s)
		{
			return  i;
		}
	}
	return -1;
}


//图初始化
Graph* Init_Graph(void)
{
	Graph* G = malloc(sizeof(*G));
	G->arcn = G->vexn = 0;

	Vtype d;

	while (1) //键盘输入数组
	{
		scanf("%c", &d);

		if (d == '#')
		{
			break;
		}
		G->g[G->vexn].data = d;
		G->g[G->vexn].first = NULL;
		G->vexn++;
	}
	getchar(); //过滤\n

	//构造顶点发出的边组成的各个单链表
	Vtype s, e;
	Adjtype w;
	while (1)
	{
		scanf("%c%c%d", &s, &e, &w);

		if (s == '#')
		{
			break;
		}
		Bian* p = malloc(sizeof(*p));
		p->next = NULL;
		p->stop_index = Find_index(G, e); //终点元素下标
		p->w = w;

		int s_i= Find_index(G, s); //起点元素下标

		if (G->g[s_i].first == NULL) //初始为空
		{
			G->g[s_i].first = p;
		}
		else
		{
			//找到插入位置进行插入操作
			Bian* pk = G->g[s_i].first;
			Bian* pr = NULL;
			while (pk)
			{
				if (pk->stop_index > p->stop_index) 
				{
					break;
				}
				pr = pk;
				pk = pk->next;
			}

			if (pk == NULL) //尾插
			{
				pr->next = p;
			}
			else if (pk == G->g[s_i].first) //头插
			{
				p->next = pk;
				G->g[s_i].first = p;
			}
			else
			{
				pr->next = p;
				p->next = pk;
			}
		}

		//过滤掉\n
		getchar();
	}
	return G;
}
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ChampLixxx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值