数据结构—图

一、图的介绍

​ 图是一种比较复杂的数据结构,在线性表中数据元素之间仅有线性关系,每个元素只有一个直接前驱和直接后继(元素之间只存在一对一关系),在树形结构中元素之间有着明显的层次关系,每一层的元素只能和下层的多个元素有关系(元素之间存在一对多关系),而在图形结构中,任意两个结点之间都可能有关系(元素之间存在多对多关系)。

二、图的定义和术语

顶点:图中的数据元素被称为顶点,一般使用V表示图的顶点的有穷非空集合。

:两个顶点之间的关系记作<v,w>,表示能从顶点v到达顶点w,也就是v能到w,但w不一定能到v,我们称v为弧尾或初始点,称w为弧头或终端点。

有向图:由弧+顶点构成的图叫有向图,也就是顶点之间是单行道。

:两个顶点之间的关系记录(v,w),表示既能从v到w,也能从w到v,我们称v和w之间的关系是一条边。

无向图:由边+顶构成的图有无向图,顶点之间的双行道。

注意:一般使用G代表图,V顶点的集合,VR代表弧或边的集合,n代表顶点的数目,e代表边或弧的数目,我们不讨论顶点到自己的边或弧。

完全图:在无向图中,e的取值范围是0 ~ n/2(n-1),如果无向图的边的数量达到最大值这种无向图称称为完全图。

有向完全图:在有向图中,e的取值范围是0 ~ n(n-1),如果有向图的弧的数量达到最大值这种有向图称称为有向完全图。

稀疏图和稠密图:如果图中的边和弧很少,e<nlogn 这种图被称为稀疏图,反之称为稠密图。

权和网:如果图中的顶点到另一个顶点需要代价(距离或耗费),那么在表示边或弧的时候需要附加数据,附加的数据就叫做权,带权的图通常被称为网,这也是互联网的由来。

子图:假定有两个图G1和G2,如果G1的顶点集合是G2的顶点集合的子集,且G1的边或弧集合是G2的边或弧集合的子集,则称G1是G2的子图。

邻接点:在无向图中如果有一条边(v,w),则v,w两个顶点互为邻接点,即v和w相邻接,边(v,w)依附于顶点v,w,或者说(v,w)和顶点v、w相关联。

顶点v的度:在无向图中与顶点v相关联的边的数量。

顶点v的入度和出度:在有向图中,以顶点v作为弧头的弧的数量称为顶点的入度,以顶点v作为弧尾的弧的数量称为顶点的出度。

路径:从顶点v到达顶点w所经历的顶点序叫做路径,路径的长度就是边或弧的数目。

回路或环:起始点和终点相同的路径称为回路或环。

简单路径:路径中顶点不重复出现的路径称为简单路径。

简单回路或简单环:起始点和终点相同且其余顶点不重复出现,被称为简单回路或简单环。

连通图:在无向图中,从顶点v到顶点w有路径,则称v和w是连通的,如果图中任意两个顶点都是连通的,则称图为连通图。

连通分量:G1和G2都是连通图,且G1是G2的子图,则称G1是G2的连通分量或极大连通子图。

强连通图:在有向图中,如果任意一对顶点都双向连通,则称图为强连通图。

强连通分量:G1和G2都是强连通图,且G1是G2的子图,则称G1是G2的强连通分量或极大强连通子图。

三、图的存储结构:

邻接矩阵:

​ 使用两个数组来存储数据元素(顶点)和数据元素之间的关系(边或弧)的信息。

一维数组:用于存储顶点。

二维数组:用于存储弧或边。

邻接矩阵的优点:

​ 访问速度快,方便计算结点的度、入度、出度。

邻接矩阵的缺点:

​ 顶点的容量有限,扩容非常麻烦,只适合存储稠密图,存储稀疏图时会有大量的内存浪费。

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// 无向图
typedef struct Grahh
{
   
	size_t cal;	  // 顶点容量
	size_t cnt;	  // 顶点数量
	char *vertex; // 存储顶点的一维数组
	char **edge;  // 存储边的二维数组
} Graph;

Graph *create_graph(size_t cal)
{
   
	Graph *graph = malloc(sizeof(Graph));
	graph->vertex = malloc(sizeof(char) * cal);
	graph->cal = cal;
	graph->cnt = 0;

	graph->edge = malloc(sizeof(char *) * cal);
	for (int i = 0; i < cal; i++)
	{
   
		graph->edge[i] = calloc(sizeof(char), cal);
	}

	return graph;
}

bool add_vertex_graph(Graph *graph, char vertex)
{
   
	if (graph->cnt >= graph->cal)
		return false;

	for (int i = 0; i < graph->cnt; i++)
	{
   
		if (graph->vertex[i] == vertex)
			return false;
	}

	graph->vertex[graph->cnt++] = vertex;
	return true;
}

bool add_edge_graph(Graph *graph, char v1, char v2)
{
   
	int v1_index = -1, v2_index = -1;
	for (int i = 0; i < graph->cnt; i++)
	{
   
		if (graph->vertex[i] == v1)
			v1_index = i;
		if (graph->vertex[i] == v2)
			v2_index = i;
	}

	if (-1 == v1_index || -1 == v2_index || v1_index == v2_index)
		return false;

	graph->edge[v1_index][v2_index] = 1;
	graph->edge[v2_index][v1_index] = 1;
	return true;
}

// 查询顶点在一维数组中的位置,如果不存在则返回-1
int query_vertex_graph(Graph *graph, char vertex)
{
   
	for (int i = 0; i < graph->cnt; i++)
	{
   
		if (vertex == graph->vertex[i])
			return i;
	}
	return -1;
}

// 查询顶点第一个邻接点,如果不存在则返回'\0'
char first_vertex_graph(Graph *graph, char vertex)
{
   
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return '\0';

	for (int i = 0; i < graph->cnt; i++)
	{
   
		if (1 == graph->edge[index][i])
			return graph->vertex[i];
	}

	return '\0';
}

// 删除顶点
bool del_vertex_graph(Graph *graph, char vertex)
{
   
	int index = query_vertex_graph(graph, vertex);
	if (-1 == index)
		return false;

	for (int i = 0; i < graph->cnt; i++)
	{
   
		graph->edge[index][i] = 0;
		graph->edge[i][index] = 0;
	}

	return true;
}

void _dfs_graph(Graph *graph, int index, bool *flags)
{
   
	if (!flags[index])
		return;

	printf("%c\n", graph->vertex[index]);
	flags[index] = false;

	for (int i = 0; i < graph->cnt; i++)
	{
   
		if (1 == graph->edge[index][i])
			_dfs_graph(graph, i, flags);
	}
}

// 深度优先遍历,与树的前序遍历类似
void dfs_graph(Graph *graph)
{
   
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	for (int i = 0; i < graph->cnt; i++)
		_dfs_graph(graph, i, flags);
}

// 广度优先遍历,与树的层序遍历一样,需要队列结构配合
void bfs_graph(Graph *graph)
{
   
	bool flags[graph->cnt];
	for (int i = 0; i < graph->cnt; i++)
		flags[i] = true;

	int queue[graph->
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值