【数据结构】无向图创建邻接矩阵、深度优先遍历和广度优先遍历(C语言版)

无向图创建邻接矩阵、深度优先遍历和广度优先遍历
  • 一、概念解析:
    • (1)无向图:
    • (2)邻接矩阵:
  • 二、创建邻接矩阵:
  • 三、深度遍历、广度遍历
    • (1)深度遍历概念:
    • (2)广度遍历概念:
  • 四、实例展示

一、概念解析:

(1)无向图:

假设图G由两个集合V和E组成,记为G={V , E}。其中V是顶点的有限集合,E是连接V中两个不同顶点的边的有限集合。如果E中的顶点对是有序的,即E中的每条边都是有方向的,则称G是有向图。如果顶点对是无序的,则称G是无向图

在这里插入图片描述

在这里插入图片描述

(2)邻接矩阵:

邻接矩阵主要由:二维数组 实现

如图
在这里插入图片描述

转换成邻接矩阵为:

在这里插入图片描述

二、创建邻接矩阵:

基本每一步都有注释,详细观看,建议画图理解

代码如下:

#define MAXSIZE 100 
//	邻接矩阵 
typedef struct Matrix{int V_Data;		//	顶点数据域 int E_Data;		//	边数数据域int Node[MAXSIZE];	//	存放顶点数据,也就是顶点表 int Weight[MAXSIZE][MAXSIZE]; 	//	存放权重,为矩阵中两点有边的标记符号 }MaTrix,*MATRIX;//	邻接矩阵数据结构体 
typedef struct Edge{int v1;		//	用来存放第一个顶点 int v2;		//	用来存放第二个顶点int weight;	//	用来存放两点之间的标记符,即为权 
}*EDGE;//******************** 邻接矩阵*******************//
//	邻接矩阵、顶点和边初始化 
void Init_Matrix(MATRIX S,int Vertex)
{	S->E_Data = 0;			//	初始化为0条边 S->V_Data = Vertex;		//	初始化顶点数 int i,j;for(i=0;i<Vertex;i++){for(j=0;j<Vertex;j++){S->Weight[i][j] = 0;}} 
}//	开始插入边的权重,即为两个顶点之间边的标记符
void InSerData(MATRIX S,EDGE E)
{//	将输入的顶点v1、v2之间的边,用权作为标记,在矩阵中表示//	这里是无向图,所以边没有方向,需要做标记两次(为v1-v2和v2-v1) S->Weight[E->v1][E->v2] = E->weight;	 S->Weight[E->v2][E->v1] = E->weight; 
} //	开始插入数据 
void InSerEdge_Data(MATRIX S,int edge,int V)
{int i,j;if(edge>0)	//	边数大于0的时候才插入数据 {printf("请输入顶点和权重(空格分隔!)\n");for(i=0;i<edge;i++){		EDGE E;				//分配内存,接受顶点v1,v2和权重(标记符)	E = (EDGE)malloc(sizeof(struct Edge));	scanf("%d %d %d",&(E->v1),&(E->v2),&(E->weight));if(E->v1 ==E->v2){printf("无向图邻接矩阵对角线为0,输入错误,结束运行\n");exit(-1); }InSerData(S,E);}	printf("请输入要定义的顶点,填入顶点表中: \n");for(j=0;j<V;j++){scanf("%d",&(S->Node[j]));}		}else{	printf("输入的边数错误"); } 		
} 

三、深度遍历、广度遍历

(1)深度遍历概念:

在这里插入图片描述

在这里插入图片描述

定义的结构体、数组可看上面代码

深度遍历代码解析:

//*****************	深度优先遍历算法—邻接矩阵 *****************//
void DFS_Begin(MATRIX P,int k,int V)
{int i;flag[k] = 1;	//标记当前顶点,表示已经遍历过printf("%d ",P->Node[k]);	//	输出当前顶点 for(i=0;i<V;i++){if(!flag[i] && P->Weight[k][i] != 0)//	如果当前顶点的邻近点存在,且没有遍历过 {									//	则继续递归遍历 DFS_Begin(P,i,V);		//	递归遍历当前顶点的邻近点 }	} 
}void Init_DFSMatrix(MATRIX P,int V)
{int i;//	初始化标记符数组,全为0 for(i=0;i<V;i++){flag[i] = 0;}for(i=0;i<V;i++)	//	每个顶点都要检查是否遍历到 {if(!flag[i])	//	排除遇到已经遍历的顶点DFS_Begin(P,i,V);		//	开始深度遍历	} putchar('\n');	
}

(2)广度遍历概念:

在这里插入图片描述

这里使用到了链队列(也可以使用数组队列,看个人想法),可以看我之前的博文有讲:

//******************** 队列 *****************//
typedef struct Queue{int data[MAXSIZE];	//	队列大小 int head;	//	队头 int wei;	//	队尾 }Queue; //*****************	队列 *************************************//
//	队列初始化 
void InitQueue(Queue *q)
{q->head= 0;		//	初始化队头、队尾 q->wei = 0;
} //	判断队列是否为空
int EmptyQueue(Queue *q)
{if(q->head == q->wei)return 1;else{return 0;}		
} //	入队
void PushQueue(Queue *q,int t)
{if((q->wei+1)%MAXSIZE == q->head)	//	说明队列已经满了return;else{	q->data[q->wei] = t;	q->wei = (q->wei +1)%MAXSIZE;	//	队尾后移 }} //	出队
void PopQueue(Queue *q,int *x)
{if(q->wei == q->head)	//	出队完毕 return;	else{	 	*x = q->data[q->head];q->head = (q->head + 1)%MAXSIZE; //	队头后移	}	} //***************** 广度优先搜索算法—邻接矩阵 ****************//
void Init_Bfs(MATRIX S,int V)
{int i,j;int k;Queue Q;for(i=0;i<V;i++){Vist[i] = 0;	//	初始化标记符 }InitQueue(&Q);	//	队列初始化 for (i = 0; i < V; i++){if (!Vist[i])	//	判断以这个顶点为基准,有连接的其他顶点 {Vist[i] = 1;	//	标记遍历的这个顶点 printf("%d ", S->Node[i]);PushQueue(&Q, i);	//	入队 while (!EmptyQueue(&Q))	//	队列中还有数据,说明这个顶点连接的其他顶点还没有遍历完 {PopQueue(&Q,&i);	//	出队 for (j = 0; j < V; j++){//	以这个顶点为基准,遍历其他连接的顶点 if (!Vist[j] && S->Weight[i][j] != 0){Vist[j] = 1;	//	与之连接的顶点作上标记,便于后序顶点跳过相同的遍历 printf("%d ", S->Node[j]);//	输出与之相邻连接的顶点 PushQueue(&Q, j);	//	让与之连接的顶点其位置入队 }}}}}
} 

四、实例展示

注意:这里存入数据时,坐标点以原点(0,0)为起点开始!

以这个图为样例展示:

在这里插入图片描述

全部代码:

#include<stdio.h>
#include<stdlib.h>#define MAXSIZE 100 //	深度遍历标记符
int flag[MAXSIZE]; 		//	邻接矩阵 //	广度优先遍历标记符 
int Vist[MAXSIZE]; 		//	邻接矩阵//******************** 队列 *****************//
typedef struct Queue{int data[MAXSIZE];	//	队列大小 int head;	//	队头 int wei;	//	队尾 }Queue; //	邻接矩阵 
typedef struct Matrix{int V_Data;		//	顶点数据域 int E_Data;		//	边数数据域int Node[MAXSIZE];	//	存放顶点数据,也就是顶点表 int Weight[MAXSIZE][MAXSIZE]; 	//	存放权重,为矩阵中两点有边的标记符号 }MaTrix,*MATRIX;//	邻接矩阵数据结构体 
typedef struct Edge{int v1;		//	用来存放第一个顶点 int v2;		//	用来存放第二个顶点int weight;	//	用来存放两点之间的标记符,即为权 
}*EDGE;//******************** 邻接矩阵*******************//
//	邻接矩阵、顶点和边初始化 
void Init_Matrix(MATRIX S,int Vertex)
{	S->E_Data = 0;			//	初始化为0条边 S->V_Data = Vertex;		//	初始化顶点数 int i,j;for(i=0;i<Vertex;i++){for(j=0;j<Vertex;j++){S->Weight[i][j] = 0;}} 
}//	开始插入边的权重,即为两个顶点之间边的标记符
void InSerData(MATRIX S,EDGE E)
{//	将输入的顶点v1、v2之间的边,用权作为标记,在矩阵中表示//	这里是无向图,所以边没有方向,需要做标记两次(为v1-v2和v2-v1) S->Weight[E->v1][E->v2] = E->weight;S->Weight[E->v2][E->v1] = E->weight; 
} //*****************	深度优先遍历算法—邻接矩阵 *****************//
void DFS_Begin(MATRIX P,int k,int V)
{int i;flag[k] = 1;	//标记当前顶点,表示已经遍历过printf("%d ",P->Node[k]);	//	输出当前顶点 for(i=0;i<V;i++){if(!flag[i] && P->Weight[k][i] != 0)//	如果当前顶点的邻近点存在,且没有遍历过 {									//	则继续递归遍历 DFS_Begin(P,i,V);		//	递归遍历当前顶点的邻近点 }	} 
}void Init_DFSMatrix(MATRIX P,int V)
{int i;//	初始化标记符数组,全为0 for(i=0;i<V;i++){flag[i] = 0;}for(i=0;i<V;i++)	//	每个顶点都要检查是否遍历到 {if(!flag[i])	//	排除遇到已经遍历的顶点DFS_Begin(P,i,V);		//	开始深度遍历	} putchar('\n');}//*****************	队列 *************************************//
//	队列初始化 
void InitQueue(Queue *q)
{q->head= 0;		//	初始化队头、队尾 q->wei = 0;
} //	判断队列是否为空
int EmptyQueue(Queue *q)
{if(q->head == q->wei)return 1;else{return 0;}		
} //	入队
void PushQueue(Queue *q,int t)
{if((q->wei+1)%MAXSIZE == q->head)	//	说明队列已经满了return;else{	q->data[q->wei] = t;	q->wei = (q->wei +1)%MAXSIZE;	//	队尾后移 }} //	出队
void PopQueue(Queue *q,int *x)
{if(q->wei == q->head)	//	出队完毕 return;	else{*x = q->data[q->head];q->head = (q->head + 1)%MAXSIZE; //	队头后移}	} //***************** 广度优先搜索算法—邻接矩阵 ****************//
void Init_Bfs(MATRIX S,int V)
{int i,j;int k;Queue Q;for(i=0;i<V;i++){Vist[i] = 0;	//	初始化标记符 }InitQueue(&Q);	//	队列初始化 for (i = 0; i < V; i++){if (!Vist[i])	//	判断以这个顶点为基准,有连接的其他顶点 {Vist[i] = 1;	//	标记遍历的这个顶点 printf("%d ", S->Node[i]);PushQueue(&Q, i);	//	入队 while (!EmptyQueue(&Q))	//	队列中还有数据,说明这个顶点连接的其他顶点还没有遍历完 {PopQueue(&Q,&i);	//	出队 for (j = 0; j < V; j++){//	以这个顶点为基准,遍历其他连接的顶点 if (!Vist[j] && S->Weight[i][j] != 0){Vist[j] = 1;	//	与之连接的顶点作上标记,便于后序顶点跳过相同的遍历 printf("%d ", S->Node[j]);//	输出与之相邻连接的顶点 PushQueue(&Q, j);	//	让与之连接的顶点其位置入队 }}}}}
} //	初始化顶点个数 
int Init_Vertex()
{int Vertex;printf("请输入顶点个数: ");scanf("%d",&Vertex);return Vertex;
}//	初始化边的数量 
int Init_Edge()
{int edge;printf("请输入边的数量: ");scanf("%d",&edge);return edge;} //	开始插入数据 
void InSerEdge_Data(MATRIX S,int edge,int V)
{int i,j;if(edge>0)	//	边数大于0的时候才插入数据 {printf("请输入顶点和权重(空格分隔!)\n");for(i=0;i<edge;i++){		EDGE E;				//分配内存,接受顶点v1,v2和权重(标记符)	E = (EDGE)malloc(sizeof(struct Edge));	scanf("%d %d %d",&(E->v1),&(E->v2),&(E->weight));if(E->v1 ==E->v2){printf("无向图邻接矩阵对角线为0,输入错误,结束运行\n");exit(-1); }InSerData(S,E);}	printf("请输入要定义的顶点,填入顶点表中: \n");for(j=0;j<V;j++){scanf("%d",&(S->Node[j]));}}else{printf("输入的边数错误"); } } //	打印无向图邻接矩阵 
void Show_Matrix(MATRIX p,int Vertex)
{int i,j;for(i=0;i<Vertex;i++){for(j=0;j<Vertex;j++){printf("%4d",p->Weight[i][j]);	//	打印邻接矩阵 }	putchar('\n');	//	换行 }
}int main()
{int val;int Vertex;int edge;MATRIX p;		//	邻接矩阵头节点指针//	创建无向图邻接矩阵 					Vertex = Init_Vertex();edge = Init_Edge();p = (MATRIX)malloc(sizeof(MaTrix));		//分配内存空间 p->V_Data = Vertex;		//	记录顶点个数 p->E_Data = edge;		//	记录边的个数 Init_Matrix(p,Vertex);	//	初始化邻接矩阵 InSerEdge_Data(p,edge,Vertex);	//	插入数据 //	打印无向图的邻接矩阵 printf("无向图邻接矩阵如下:");	printf("\n----------------------------------\n\n");Show_Matrix(p,Vertex);printf("\n----------------------------------\n");//	深度优先遍历—邻接矩阵	printf("深度遍历—邻接矩阵结果为:\n");Init_DFSMatrix(p,Vertex);//	广度优先遍历—邻接矩阵printf("广度优先遍历—邻接矩阵结果为: \n");Init_Bfs(p,Vertex);return 0;	
} 

结果图:
在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[职场] 实验室科研人员简历范文 #学习方法#职场发展

实验室科研人员简历范文 想要成为一名实验室科研人员&#xff0c;我们应该怎么制作简历呢&#xff1f;下面是实验室科研人员简历范文&#xff0c;供大家参考。 一、基本信息 姓名&#xff1a;文书帮 工作经验&#xff1a;两年以上 性别&#xff1a;男 年龄&#xff1a;26岁…

Fluke ADPT 连接器新增对福禄克万用 Fluke 107 的支持

所需设备&#xff1a; 1、Fluke ADPT连接器&#xff1b; 2、Fluke 107&#xff1b; Fluke 107 拆机图&#xff1a; 显示界面如下图&#xff1a; 并且可以将波形导出到EXCEL: 福禄克万用表需要自己动手改造&#xff01;&#xff01;&#xff01;

HCIA-HarmonyOS设备开发认证V2.0-内核扩展组件

目录 一、CPU 占用率1.1、CPU 占用率基本概念1.2、CPU 占用率运行机制1.3、CPU 占用率开发流程 二、动态加载2.1、 动态加载基本概念2.2、动态加载运行机制 坚持就有收获 一、CPU 占用率 1.1、CPU 占用率基本概念 CPU&#xff08;中央处理器&#xff0c;Central Processing U…

Fluke ADPT 连接器新增对福禄克万用 Fluke 101 的支持

所需设备&#xff1a; 1、Fluke ADPT连接器&#xff1b; 2、Fluke 101&#xff1b; Fluke 101 拆机图&#xff1a; 显示界面如下图&#xff1a; 并且可以将波形导出到EXCEL: 福禄克万用表需要自己动手改造&#xff01;&#xff01;&#xff01;

day29 回溯part5

491. 非递减子序列 中等 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重复元素&#xff0c;如出现两个整数相等&#xff0c;也可以视作递增序列的一种特…

【Python】通过conda安装Python的IDE

背景 系统&#xff1a;win11 软件&#xff1a;anaconda Navigator 问题现象&#xff1a;①使用Navigator安装jupyter notebook以及Spyder IDE 一直转圈。②然后进入anaconda prompt执行conda install jupyter notebook一直卡在Solving environment/-\。 类似问题&#xff1a; …

大学建筑专业的搜题软件?大学搜题工具中的高级搜索功能有哪些? #学习方法#微信#经验分享

学习和考试是大学生生活中不可避免的一部分&#xff0c;而在这个信息爆炸的时代&#xff0c;如何快速有效地获取学习资源和解答问题成为了大学生们共同面临的难题。为了解决这个问题&#xff0c;搜题和学习软件应运而生。今天&#xff0c;我将为大家介绍几款备受大学生青睐的搜…

线程池(图解,本质,模拟实现代码)

目录 线程池 介绍 图解 过程 本质 模拟实现 思路 注意点 解决方法 代码 pthread_pool.hpp task.hpp main.cpp 示例 线程池 介绍 线程池是一种并发编程的设计模式&#xff0c;用于管理和重复使用线程&#xff0c;以提高多线程应用程序的性能和效率 线程池主要用于…

【C++】C++11上

C11上 1.C11简介2.统一的列表初始化2.1 {} 初始化2.2 initializer_list 3.变量类型推导3.1auto3.2decltype3.3nullptr 4.范围for循环5.final与override6.智能指针7. STL中一些变化8.右值引用和移动语义8.1左值引用和右值引用8.2左值引用与右值引用比较8.3右值引用使用场景和意义…

【网站项目】023实验室耗材管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

Spring中的事务实现、失效场景即AOP的相关概念理解

spring实现事务&#xff08;声明式事务&#xff09;的本质就是aop完成的,它会对方法的前后进行拦截,在执行方法之前开启事务,在执行完目标方法之后根据执行情况提交或回滚事务。aop就是面向切面编程&#xff0c;在spring中将那些与业务无关&#xff0c;但却对多个对象产生影响的…

STM32,嵌入式系统中的I2C协议

I2C协议——读写EEPROM 关注我&#xff0c;共同交流&#xff0c;一起成长 前言一、协议简介二、I2C特性及架构三、通信过程 前言 这是一种主要用于集成电路和集成电路&#xff08;IC&#xff09;通信&#xff0c;计算机中复杂的问题大多数就是用分层来进行解决&#xff0c;这个…