【数据结构】C语言实现图的相关操作

news2024/10/7 8:31:33

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

术语

无向图:每条边都是无方向的图

有向图:每条边都是有方向的图
在这里插入图片描述

完全图:任意两个点都有一条边相连的图

边:无向图中的边

弧:有向图中的边

稀疏图:有很少边或弧的图

稠密图:有较多边或弧的图

网:边/弧带权的图(带权的意思就是边/弧上有数值)

邻接:有边/弧相连的两个顶点间的关系称为邻接

顶点的度:与该顶点相连的边的数目

在有向图中,顶点的度等于该顶点的入度和出度之和

顶点的入度是以该顶点为终点的弧的条数

顶点的出度是以该顶点为起点的弧的条数

路径:连续的边所构成的顶点序列

路径长度:路径上边/弧的数目

回路(环):第一个顶点和最后一个顶点相同的路径

简单路径:除路径起点和终点可以相同外,其余顶点均不相同的路径

简单回路(环):除路径起点和终点相同外,其余顶点均不相同的路径

连通图:在无向图中,从任意一个顶点出发,可以到达其余任意顶点

强连通图:在有向图中,从任意一个顶点出发,可以到达其余任意顶点

权:图中边或弧所具有的相关树称为权,表明从一个顶点到另一个顶点的距离或耗费

网:带权的图称为网

存储结构

  • 顺序存储结构

邻接矩阵(数组表示法)

  • 链式存储结构

邻接表(链式表示法)

邻接矩阵

设图的顶点数量为 n,邻接矩阵使用一个 n * n 大小的矩阵(二维数组)来表示图,每一行(列)代表一个顶点,用1或0表示两个顶点之间是否存在边

建立一个顶点表(记录各个顶点信息)和一个邻接矩阵(表示各个顶点之间关系)

无向图:
在这里插入图片描述

有向图:
在这里插入图片描述

有向图的邻接矩阵中,行号表示弧的起点,列号表示弧的终点

如果想要表示网,则将邻接矩阵中的0换成无穷大即可

表示
#define MAX_INT 2147483647
#define MAX_V_NUM 100

typedef struct AMGraph
{
	char vexs[MAX_V_NUM];
	int arcs[MAX_V_NUM][MAX_V_NUM];
	int vex_num;
	int arc_num;
}AMGraph;

MAX_INT:表示极大值,即无穷大,用在网中

MAX_V_NUM:表示最大顶点树

AMGraph:Adjacency Matrix Graph 邻接矩阵图

char vexs[MAX_V_NUM]:顶点表

int arcs[MAX_V_NUM][MAX_V_NUM]:邻接矩阵,表示顶点间的关系

int vex_num:图的顶点数

int arc_num:图的边数

创建无向图

思路:

  1. 输入图的顶点数和边数
  2. 将点的信息存入顶点表中
  3. 初始化邻接矩阵
  4. 构造邻接矩阵,将边的信息存入矩阵中
void create_UDG(AMGraph* G)
{
	//输入图的顶点数和边数
	printf("Enter vex num:");
	scanf("%d", &G->vex_num);
	printf("Enter arc num:");
	scanf("%d", &G->arc_num);
	//将点的信息存入顶点表中(所谓的点的信息,其实就是点的名字)
	int i = 0;
	int j = 0;
	for (i = 0; i < G->vex_num; i++)
	{
		printf("Enter information for the %d-th vertex:", i + 1);
		scanf(" %c", &G->vexs[i]);
	}

	//初始化邻接矩阵
	for (i = 0; i < G->vex_num; i++)
	{
		for (j = 0; j < G->vex_num; j++)
		{
			G->arcs[i][j] = 0;	//网:MAX_INT,图:0
		}
	}

	//构造邻接矩阵
	for (i = 0; i < G->arc_num; i++)
	{
		char v1 = 0;
		char v2 = 0;
		//int w = 0;
		printf("Input two connected vertices:");
		scanf(" %c %c", &v1, &v2);

		//printf("Enter the starting point of the edge:");
		//scanf(" %c", &v1);
		//printf("Enter the endpoint of the edge:");
		//scanf(" %c", &v2);
		//printf("Input the weight of the edge:");
		//scanf("%d", &w);

		int i = locate_vex(G, v1);
		int j = locate_vex(G, v2);
		//G->arcs[i][j] = w;
		G->arcs[i][j] = 1;
		G->arcs[j][i] = G->arcs[i][j];
	}
}

int locate_vex(AMGraph* G, char v)
{
	int i = 0;
	for (i = 0; i < G->vexs; i++)
	{
		if (v == G->vexs[i])
		{
			return i;
		}
	}
	return -1;
}

以上代码,进行适当的修改,可以用来创建有向图、无向网、有向网

邻接表

在这里插入图片描述

邻接表(adjacency list)使用 n 个链表来表示图,链表结点表示顶点。链表中存储了该顶点的所有邻接顶点(与该顶点相连的顶点)
在这里插入图片描述

顶点表中的结点分为数据域和链域,数据域存储顶点信息,链域指向链表中的第一个结点

链表中的结点分为邻接点域、链域和数据域,邻接点域表示该顶点,存储顶点在顶点表中的位置,链域指向下一个相连的顶点,数据域存储与边有关的信息(权值)
在这里插入图片描述

在有向图中,链接的都是弧指向的顶点(如果反过来,那就是逆邻接表)

表示
#define MAX_INT 2147483647
#define MAX_V_NUM 100

//ArcNode 表示链表中的结点
typedef struct ArcNode
{
	int adjvex;				//该顶点在顶点表中的位置
	struct ArcNode* nextarc;//指向下一个相连的顶点
	int info;				//存储与边有关的信息(权值)
}ArcNode;

//VNode 表示顶点表中的结点
typedef struct VNode
{
	char data;				//顶点信息
	ArcNode* firstarc;		//指向与该顶点第一个相连的顶点
}VNode, AdjList[MAX_V_NUM];	//AdjList 表示顶点表

//ALGraph 用邻接表表示的图
typedef struct ALGraph
{
	AdjList vertices;	//顶点表 verticse是vertex的复数
	//相当于AdjList vertices[MAX_V_NUM]
	int vex_num;		//图的顶点数
	int arc_num;		//图的边数
}ALGraph;
创建无向图

思路:

  1. 输入顶点数和边数
  2. 建立顶点表(输入顶点信息,指针域置NULL)
  3. 创建邻接表
void create_UDG(ALGraph* G)
{
	//输入图的顶点数和边数
	printf("Enter vex num:");
	scanf("%d", &G->vex_num);
	printf("Enter arc num:");
	scanf("%d", &G->arc_num);

	//将点的信息存入顶点表中
	int i = 0;
	int j = 0;
	for (i = 0; i < G->vex_num; i++)
	{
		printf("Enter information for the %d-th vertex:", i + 1);
		scanf(" %c", &G->vertices[i].data);
		G->vertices[i].firstarc = NULL;
	}
	//创建邻接表
	for (i = 0; i < G->arc_num; i++)
	{
		char v1 = 0;
		char v2 = 0;
		printf("Input two connected vertices:");
		scanf(" %c %c", &v1, &v2);
		int i = locate_vex(G, v1);
		int j = locate_vex(G, v2);

		ArcNode* p1 = (ArcNode*)malloc(sizeof(ArcNode));
		if (p1 == NULL)
		{
			exit(0);
		}
		ArcNode* p2 = (ArcNode*)malloc(sizeof(ArcNode));
		if (p2 == NULL)
		{
			exit(0);
		}
		//p1结点是和顶点v1相连的结点,所以p1结点存的是顶点v2的信息
		p1->adjvex = j;
		//头插法建立链表,因此,最后的输出的次序和输入的次序是相反的(DFS、BFS)
		p1->nextarc = G->vertices[i].firstarc;
		G->vertices[i].firstarc = p1;

		p2->adjvex = i;
		p2->nextarc = G->vertices[j].firstarc;
		G->vertices[j].firstarc = p2;
	}
}

int locate_vex(ALGraph* G, char v)
{
	int i = 0;
	for (i = 0; i < G->vex_num; i++)
	{
		if (v == G->vertices[i].data)
		{
			return i;
		}
	}
	return -1;
}

优缺点

邻接矩阵

  • 优点

方便查找”邻接点“

方便计算顶点的”度“

  • 缺点

不便于增加和删除顶点

浪费空间,如果是稀疏图,会有大量的空间浪费

邻接表

  • 优点

方便查找”邻接点“

节约空间

  • 缺点

不便于检查任意一对顶点间是否存在边

邻接矩阵多用于稠密图,邻接表多用于稀疏图

图的遍历

遍历:从已给出的连通图中的某一顶点出发,沿着一些边访遍图中的所有顶点,且每个顶点仅被访问一次

以下代码都是假设所要搜索的图是无向图

深度优先搜索

深度优先搜索(Depth First Search)(DFS)

思路:

构造一个辅助数组 visited[i],用来标记每个被访问过的顶点

在访问图中某一起始顶点 v 后,由 v 出发,访问它的任一邻接顶点 w1

再从 w1 出发,访问与 w1 邻接但未被访问过的顶点 w2

再从 w2 出发,进行类似的访问

如此反复,直至到达所有的邻接顶点都被访问过的顶点 u 为止

接着,回退一步,退到前一次访问的顶点,看是否还有其他未被访问的邻接顶点

如果有,访问该顶点,再从该顶点出发,进行访问

如果没有,继续回退,重复之前步骤,直到图中所有的顶点都被访问过

例如:
在这里插入图片描述

对于该图的深度优先搜索,有如下访问次序:

v1、v2、v4、v8、v5、v3、v6、v7

v1、v3、v6、v7、v2、v4、v8、v5

基于邻接矩阵

void DFS(AMGraph* G, char v, int visited[MAX_V_NUM])
{
	printf("%c ", v);
	int i = locate_vex(G, v);
	visited[i] = 1;

	int j = 0;
	for (j = 0; j < G->vex_num; j++)
	{
		if (G->arcs[i][j] != 0 && visited[j] != 1)
		{
			char next_v = G->vexs[j];
			DFS(G, next_v, visited);
		}
	}
}

基于邻接表

void DFS(ALGraph* G, char v, int visited[MAX_V_NUM])
{
	int i = locate_vex(G, v);
	visited[i] = 1;
	printf("%c ", v);

	ArcNode* p = G->vertices[i].firstarc;
	while (p != NULL)
	{
		char next_v = G->vertices[p->adjvex].data;
		i = locate_vex(G, next_v);
		if (visited[i] == 0)
		{
			DFS(G, next_v, visited);
		}
		p = p->nextarc;
	}
}

广度优先搜索

广度优先搜索(Breadth First Search)(BFS)

思路:

从图的某一顶点出发,首先依次访问该顶点的所有邻接点,再按访问这些邻接点的顺序,依次访问与它们相邻接的且未被访问过的顶点,重复此过程,直到所有顶点被访问为止

例如:
在这里插入图片描述

访问次序:

v1、v2、v3、v4、v5、v6、v7、v8

基于邻接矩阵

void BFS(AMGraph* G, char v, int visited[MAX_V_NUM])
{
	printf("%c ", v);
	int i = locate_vex(G, v);
	visited[i] = 1;
	Queue Q;
	init(&Q);
	in(&Q, v);
	while (isEmpty(&Q) != 1)	//队列不为空时,循环继续
	{
		char u = out(&Q);		//队头出队
		i = locate_vex(G, u);	
		int j = 0;
		for (j = 0; j < G->vex_num; j++)	//找出队顶点的邻接点
		{
			if (G->arcs[i][j] != 0 && visited[j] != 1)
			{
				//输出顶点信息,然后该顶点入队
				char next_v = G->vexs[j];	
				printf("%c ", next_v);
				visited[j] = 1;
				in(&Q, next_v);
			}
		}
	}
}

基于邻接表

void BFS(ALGraph* G, char v, int visited[MAX_V_NUM])
{
	Queue Q;
	init(&Q);
	printf("%c ", v);
	int i = locate_vex(G, v);
	visited[i] = 1;
	in(&Q, v);

	while (isEmpty(&Q) != 1)
	{
		char u = out(&Q);
		i = locate_vex(G, u);

		ArcNode* p = G->vertices[i].firstarc;
		while (p != NULL)
		{
			if (visited[p->adjvex] == 0)
			{
				char next_v = G->vertices[p->adjvex].data;
				printf("%c ", next_v);
				visited[p->adjvex] = 1;
				in(&Q, next_v);
			}
			p = p->nextarc;
		}
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1467156.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用HiveMQ实现Android MQTT

MQTT官网&#xff1a;https://mqtt.org/ 百度Android MQTT&#xff0c;或者B站上搜索&#xff0c;发现大多使用https://github.com/eclipse/paho.mqtt.android&#xff0c;这是Eclipse的一个Android MQTT客户端实现库&#xff0c;但是我发现这个库在运行到高版本的手机上时报错…

Unet 高阶分割网络实战、多类别分割、迁移学习(deeplab、resnet101等等)

1、前言 Unet 图像分割之前介绍了不少&#xff0c;具体可以参考 图像分割专栏 为了实现多类别的自适应分割&#xff0c;前段时间利用numpy的unique函数实现了一个项目。通过numpy函数将mask的灰度值提取出来&#xff0c;保存在txt文本里&#xff0c;这样txt里面就会有类似0 1…

基于微信小程序的垃圾分类系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Java SpringBoot测试OceanBase

对上篇mysql导入到OceanBase中的数据库进行代码测试&#xff0c;写了个demo包含测试方法&#xff0c;在原mysql库中成功执行&#xff0c;迁移到OceanBase时看是否能不修改业务代码而成功执行测试方法&#xff1a; 代码基于SpringBoot MyBastis测试增删改查、批量新增、多表联…

在项目中应用设计模式的实践指南

目录 ✨✨ 祝屏幕前的您天天开心&#xff0c;每天都有好运相伴。我们一起加油&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 引言 一. 单例模式&#xff08;Singleton Pattern&#xff09; 1、实现单例模式的方式 1…

List集合之UML、特点、遍历方式、迭代器原理、泛型、装拆箱及ArrayList、LinkedList和Vector的区别

目录 ​编辑 一、什么是UML 二、集合框架 三、List集合 1.特点 2.遍历方式 3.删除 4.优化 四、迭代器原理 五、泛型 六、装拆箱 七、ArrayList、LinkedList和Vector的区别 ArrayList和Vector的区别 LinkedList和Vector的区别 一、什么是UML UML&#xff08;Unif…

20个改善编码的Python异常处理技巧,让你的代码更高效

异常处理是写好代码的一个重要的方面&#xff0c;虽然许多开发人员都熟悉基本的try-except块&#xff0c;但是有很多更深入的知识可以使异常处理更高效、更可读和更python化。所以本文将介绍关于Python异常的20个可以显著改善编码的Python异常处理技巧&#xff0c;这些技巧可以…

C/C++内存管理学习【new】

文章目录 一、C/C内存分布二、C语言中动态内存管理方式&#xff1a;malloc/calloc/realloc/free三、C内存管理方式3.1 new/delete操作内置类型3.2 new和delete操作自定义类型四、operator new与operator delete函数五、new和delete的实现原理5.1 内置类型 六、定位new表达式(pl…

天锐绿盾 | 文件数据\资料防泄漏软件 \ 自动智能透明加密保护

怎么防止公司办公终端文件数据资料外泄? 防止公司办公终端文件数据资料外泄是非常重要的&#xff0c;以下是一些有效的措施&#xff1a; 限制访问权限&#xff1a;根据员工的职责和需求&#xff0c;设定文件和数据资料的访问权限。确保只有授权人员才能访问敏感信息。 加密存…

2024图像处理分析与信息工程国际学术会议(IACIPIE2024)

2024图像处理分析与信息工程国际学术会议(IACIPIE2024) 会议简介 2024图像处理分析与信息工程国际学术会议&#xff08;IACIPIE2024&#xff09;将在中国长沙举行。 IACIPIE2024是一个年度会议&#xff0c;探讨图像处理分析和信息工程相关领域的发展和影响&#xff0c;旨在介…

数字孪生低代码平台盘点(一):厂家介绍

特别说明&#xff1a;本文根据网上资料搜集整理而成&#xff0c;排名不分先后&#xff0c;配图是为了更好地阅读体验&#xff0c;并非表明该图为该平台所生产。如有错误之处&#xff0c;请在评论区提出。 一、优锘ChartBuilder 优锘ChartBuilder是一款基于Web的数据可视化工具…

C++的vector容器->基本概念、构造函数、赋值操作、容量和大小、插入和删除、数据存取、互换容器、预留空间

#include<iostream> using namespace std; #include <vector> //vector容器构造 void printVector(vector<int>& v) { for (vector<int>::iterator it v.begin(); it ! v.end(); it) { cout << *it << " "…

挑战杯 基于卷积神经网络的乳腺癌分类 深度学习 医学图像

文章目录 1 前言2 前言3 数据集3.1 良性样本3.2 病变样本 4 开发环境5 代码实现5.1 实现流程5.2 部分代码实现5.2.1 导入库5.2.2 图像加载5.2.3 标记5.2.4 分组5.2.5 构建模型训练 6 分析指标6.1 精度&#xff0c;召回率和F1度量6.2 混淆矩阵 7 结果和结论8 最后 1 前言 &…

RGB颜色如何转换为十六进制?16进制颜色代码怎么转为RGB颜色值?

我们在调整网站的色彩搭配&#xff0c;或修改图片的时候&#xff0c;偶尔需要用到RGB颜色值&#xff0c;或者16进制颜色代码。 如果我只知道16进制颜色代码想要知道RGB颜色值&#xff0c;那么16进制颜色代码怎么转为RGB颜色值&#xff1f;又或者我知道RGB颜色值想要知道16进制…

ubuntu使用LLVM官方发布的tar.xz来安装Clang编译器

ubuntu系统上的软件相比CentOS更新还是比较快的&#xff0c;但是还是难免有一些软件更新得不那么快&#xff0c;比如LLVM Clang编译器&#xff0c;目前ubuntu 22.04版本最高还只能安装LLVM 15&#xff0c;而LLVM 18 rc版本都出来了。参见https://github.com/llvm/llvm-project/…

Jenkins使用遇到的一些问题

一&#xff1a;插件依赖报错 比如遇到一堆插件报错&#xff0c;不是提示版本对不上&#xff0c;就是启用不了 这样直接把Jenkins升级就行了&#xff0c;比如我这个是命令行启动的&#xff0c;直接把他替换就好了 如果是遇到插件依赖报错&#xff0c;比如A插件异常 则点击这个插…

训练Sora模型,你可能需要这些开源代码,模型,数据集及算力评估

在之前的文章&#xff0c;我们总结了Sora模型上用到的一些核心技术和论文 复刻大模型 Sora 有多难&#xff1f;一张图带你读懂 Sora 的技术路径一文看懂大模型 Sora 技术推演 今天这篇文章来自我们社区讨论交流&#xff0c;我这边整理和总结现有的一些开源代码、模型、数据集…

网页数据的存储--存储为文本文件(TXT、JSON、CSV)

用解析器解析出数据后&#xff0c;接下来就是存储数据了。数据的存储有多种多样&#xff0c;其中最简单的一种是将数据直接保存为文本文件&#xff0c;如TXT、JSON、CSV等。这里就介绍将数据直接保存为文本文件。 目录 一、Python存储数据的方法 1、 文件读取 2、 文件写入…

milvus Delete api写s3的流程

Delete api写s3的流程 milvus版本:v2.3.2 整体架构: Delete 的数据流向 delete相关配置 dataNode:segment:insertBufSize: 16777216 # Max buffer size to flush for a single segment.deleteBufBytes: 67108864 # Max buffer size to flush del for a single channelsyncPe…

Neo4j导入数据之JAVA JDBC

目录结构 前言设置neo4j外部访问代码整理maven 依赖java 代码 参考链接 前言 公司需要获取neo4j数据库内容进行数据筛查&#xff0c;neo4j数据库咱也是头一次基础&#xff0c;辛辛苦苦安装好整理了安装neo4j的步骤&#xff0c;如今又遇到数据不知道怎么创建&#xff0c;关关难…