数据结构 | 图的遍历(C语言)

news2024/12/23 17:21:40

一、数据结构定义

1、图

#define MaxVertexNum 100 // 最大可存储的节点数目

/*图*/
typedef char VexterType;
typedef int EdgeType;

typedef struct GraphMatrix {
	VexterType Vexs[MaxVertexNum];				//结点 
	EdgeType Edges[MaxVertexNum][MaxVertexNum];	//边
	int vexnum, arcnum;							//当前点数和边数
}*MGraph;

int visited[MaxVertexNum]; // 记录是否访问该节点,访问过为1,否则为0

使用邻接矩阵法存储图的信息,其中

  • 一维矩阵 Vexs[] 存储节点信息
  • 二维矩阵 Edges[][] 存储边的信息
  • 一维矩阵 visited[] 记录当前节点是否被访问过,用于后面的遍历

本文所使用的图的结构如下:

对应的 Vexs[] 为:A,B,C,D,E,F(对应的数组下标从0到5)

对应的 Edges[][] 如下:

 2、队列

/*队列*/
typedef int QueueType;
typedef struct QueueNode {
	QueueType data;
	struct QueueNode* next;
}QueueNode;

typedef struct {
	QueueNode* front, * rear;
}LinkQueue;

图的广度优先遍历BFS需要使用队列进行辅助

二、方法概览

1、队列

void initQueue(LinkQueue* Q);//初始化队列
int isQueueEmpty(LinkQueue* Q);//判断队列是否为空
void enQueue(LinkQueue* Q, QueueType data);//入队
int deQueue(LinkQueue* Q, QueueType* data);//出队

2、图

void printGraph(MGraph G);//打印图的邻接矩阵
MGraph initGraph();//初始化图并输入数据
int adjacent(MGraph G, int x, int y);//判断是否存在某条边(x,y)
int firstNeighbor(MGraph G, int v);//求点v的第一个邻接点
int nextNeighbor(MGraph G, int v, int w);//求点v的邻接点w的下一个邻接点

void visit(MGraph G, int v);//访问该节点的信息
void BFS(MGraph G, LinkQueue Q, int v);// 广度优先遍历
void BFSTraverse(MGraph G);//广度优先遍历 主函数
void DFS(MGraph G, int v);//深度优先遍历
void DFSTraverse(MGraph G);//深度优先遍历 主函数

三、方法详解

1、队列

//初始化队列
void initQueue(LinkQueue* Q) {
	Q->front = Q->rear = (QueueNode*)malloc(sizeof(QueueNode)); // 分配头节点
	Q->front->next = NULL; //初始化为空
}
//判断队列是否为空
int isQueueEmpty(LinkQueue* Q) {
	if (Q->front == Q->rear) return 1;
	else return 0;
}
//入队
void enQueue(LinkQueue* Q, QueueType data) {
	QueueNode* news = (QueueNode*)malloc(sizeof(QueueNode));
	news->data = data; // 创建新节点,插入队列尾部 
	news->next = NULL;
	Q->rear->next = news;
	Q->rear = news;
}
//出队
int deQueue(LinkQueue* Q, QueueType* data) {
	if (Q->front == Q->rear) return 0;
	QueueNode* del = Q->front->next;
	*data = del->data;
	Q->front->next = del->next;
	if (Q->rear == del)
		Q->rear = Q->front; // 若原队列只有一个节点,删除后变空 
	free(del);
	return 1;
}

2、图

(1)基本操作

// 打印图的邻接矩阵
void printGraph(MGraph G) {
	int i, j;
	printf("G->Edges[][] = \n\t");

	for (i = 0; i < G->vexnum; ++i)
		printf(" %c \t", G->Vexs[i]);
	printf("\n");

	for (i = 0; i < G->vexnum; ++i) {
		printf(" %c \t", G->Vexs[i]);
		for (j = 0; j < G->vexnum; j++)
			printf(" %d \t", G->Edges[i][j]);
		printf("\n");
	}
}

// 初始化图并输入数据
MGraph initGraph() {
	int i, j;
	MGraph G = (MGraph)malloc(sizeof(struct GraphMatrix));
	G->vexnum = 6;
	G->arcnum = 14;

	/* 初始化图的邻接矩阵 Edges[MaxVertexNum][MaxVertexNum] */
	for (i = 0; i < G->vexnum; i++) {
		for (j = 0; j < G->vexnum; j++) {
			G->Edges[i][j] = 0;	 
		}
	}

	/* 输入图的结点矩阵 Vexs[MaxVertexNum] */
	int v[6] = { 'A','B','C','D','E','F' };
	for (i = 0; i < G->vexnum; i++)
		G->Vexs[i] = v[i];

	/* 输入图的边权 */
	int  start_vex[14] = { 0,0,0,1,1,1,2,2,3,3,4,4,5,5 };
	int    end_vex[14] = { 1,2,3,0,4,5,0,1,0,5,1,2,1,3 };
	for (i = 0; i < G->arcnum; i++)
		G->Edges[start_vex[i]][end_vex[i]] = 1;
	return G;
}

// 判断是否存在某条边(x,y)
int adjacent(MGraph G, int x, int y) {
	if (G->Edges[x][y] == 1)
		return 1;
	else
		return -1;
}

// 求点v的第一个邻接点
// 若有返回顶点号,否则返回-1
int firstNeighbor(MGraph G, int v) {
	for (int i = 0; i < G->Vexs; i++) {
		if (G->Edges[v][i] == 1)
			return i;
	}
	return -1;
}

// 求点v的邻接点w的下一个邻接点
// 若有返回顶点号,否则返回-1
int nextNeighbor(MGraph G, int v, int w) {
	if (w < G->vexnum) {
		for (int i = w + 1; i < G->vexnum; i++) {
			if (G->Edges[v][i] == 1)
				return i;
		}
		return -1;
	}
	return -1;
}

(2)图的深度优先遍历DFS

// 访问该节点的信息
void visit(MGraph G,int v) {
	printf("%c ",G->Vexs[v]);
}

// 深度优先遍历
void DFS(MGraph G, int v) {
	visit(G, v);
	visited[v] = 1;
	for (int w = firstNeighbor(G, v); w >= 0; w = nextNeighbor(G, v, w)) {
		if (visited[w] == 0)
			DFS(G, w);
	}
}

// 深度优先遍历 主函数
void DFSTraverse(MGraph G) {
	for (int i = 0; i < G->vexnum; ++i) {
		visited[i] = 0;
	}
	for (int i = 0; i < G->vexnum; ++i) {
		if (visited[i] == 0)
			DFS(G, i);
	}
}

(3)图的广度优先遍历BFS

// 访问该节点的信息
void visit(MGraph G,int v) {
	printf("%c ",G->Vexs[v]);
}

// 广度优先遍历
void BFS(MGraph G, LinkQueue Q, int v) {
	visit(G, v);
	visited[v] = 1;
	enQueue(&Q, v);
	while (!isQueueEmpty(&Q)) {
		deQueue(&Q, &v);
		for (int w = firstNeighbor(G, v); w >= 0; w = nextNeighbor(G, v, w)) {
			if (visited[w] == 0) {
				visit(G, w);
				visited[w] = 1;
				enQueue(&Q, w);
			}
		}
	}
}

// 广度优先遍历 主函数
void BFSTraverse(MGraph G) {
	for (int i = 0; i < G->vexnum; ++i) {
		visited[i] = 0;
	}
	LinkQueue Q;
	initQueue(&Q);
	for (int i = 0; i < G->vexnum; ++i) {
		if (visited[i] == 0)
			BFS(G, Q, i);
	}
}

四、运行结果

        main方法代码如下:

int main() {
	MGraph G = initGraph();
	printGraph(G);

	printf("\n广度优先遍历 : ");
	BFSTraverse(G);

	printf("\n深度优先遍历 : ");
	DFSTraverse(G);
	return 0;
}

        运行结果如下:

 五、源代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

#define MaxVertexNum 100 // 最大可存储的节点数目

/*图*/
typedef char VexterType;
typedef int EdgeType;

typedef struct GraphMatrix {
	VexterType Vexs[MaxVertexNum];				//结点 
	EdgeType Edges[MaxVertexNum][MaxVertexNum];	//边
	int vexnum, arcnum;							//当前点数和边数
}*MGraph;

int visited[MaxVertexNum]; // 记录是否访问该节点,访问过为1,否则为0


/*队列*/
typedef int QueueType;
typedef struct QueueNode {
	QueueType data;
	struct QueueNode* next;
}QueueNode;

typedef struct {
	QueueNode* front, * rear;
}LinkQueue;

void initQueue(LinkQueue* Q);//初始化队列
int isQueueEmpty(LinkQueue* Q);//判断队列是否为空
void enQueue(LinkQueue* Q, QueueType data);//入队
int deQueue(LinkQueue* Q, QueueType* data);//出队


void printGraph(MGraph G);//打印图的邻接矩阵
MGraph initGraph();//初始化图并输入数据
int adjacent(MGraph G, int x, int y);//判断是否存在某条边(x,y)
int firstNeighbor(MGraph G, int v);//求点v的第一个邻接点
int nextNeighbor(MGraph G, int v, int w);//求点v的邻接点w的下一个邻接点

void visit(MGraph G, int v);//访问该节点的信息
void BFS(MGraph G, LinkQueue Q, int v);// 广度优先遍历
void BFSTraverse(MGraph G);//广度优先遍历 主函数
void DFS(MGraph G, int v);//深度优先遍历
void DFSTraverse(MGraph G);//深度优先遍历 主函数



//初始化队列
void initQueue(LinkQueue* Q) {
	Q->front = Q->rear = (QueueNode*)malloc(sizeof(QueueNode)); // 分配头节点
	Q->front->next = NULL; //初始化为空
}
//判断队列是否为空
int isQueueEmpty(LinkQueue* Q) {
	if (Q->front == Q->rear) return 1;
	else return 0;
}
//入队
void enQueue(LinkQueue* Q, QueueType data) {
	QueueNode* news = (QueueNode*)malloc(sizeof(QueueNode));
	news->data = data; // 创建新节点,插入队列尾部 
	news->next = NULL;
	Q->rear->next = news;
	Q->rear = news;
}
//出队
int deQueue(LinkQueue* Q, QueueType* data) {
	if (Q->front == Q->rear) return 0;
	QueueNode* del = Q->front->next;
	*data = del->data;
	Q->front->next = del->next;
	if (Q->rear == del)
		Q->rear = Q->front; // 若原队列只有一个节点,删除后变空 
	free(del);
	return 1;
}


// 打印图的邻接矩阵
void printGraph(MGraph G) {
	int i, j;
	printf("G->Edges[][] = \n\t");

	for (i = 0; i < G->vexnum; ++i)
		printf(" %c \t", G->Vexs[i]);
	printf("\n");

	for (i = 0; i < G->vexnum; ++i) {
		printf(" %c \t", G->Vexs[i]);
		for (j = 0; j < G->vexnum; j++)
			printf(" %d \t", G->Edges[i][j]);
		printf("\n");
	}
}
// 初始化图并输入数据
MGraph initGraph() {
	int i, j;
	MGraph G = (MGraph)malloc(sizeof(struct GraphMatrix));
	G->vexnum = 6;
	G->arcnum = 14;

	/* 初始化图的邻接矩阵 Edges[MaxVertexNum][MaxVertexNum] */
	for (i = 0; i < G->vexnum; i++) {
		for (j = 0; j < G->vexnum; j++) {
			G->Edges[i][j] = 0;
		}
	}

	/* 输入图的结点矩阵 Vexs[MaxVertexNum] */
	int v[6] = { 'A','B','C','D','E','F' };
	for (i = 0; i < G->vexnum; i++)
		G->Vexs[i] = v[i];

	/* 输入图的边权 */
	int  start_vex[14] = { 0,0,0,1,1,1,2,2,3,3,4,4,5,5 };
	int    end_vex[14] = { 1,2,3,0,4,5,0,1,0,5,1,2,1,3 };
	for (i = 0; i < G->arcnum; i++)
		G->Edges[start_vex[i]][end_vex[i]] = 1;
	return G;
}
// 判断是否存在某条边(x,y)
int adjacent(MGraph G, int x, int y) {
	if (G->Edges[x][y] == 1)
		return 1;
	else
		return -1;
}
// 求点v的第一个邻接点
// 若有返回顶点号,否则返回-1
int firstNeighbor(MGraph G, int v) {
	for (int i = 0; i < G->vexnum; i++) {
		if (G->Edges[v][i] == 1)
			return i;
	}
	return -1;
}
// 求点v的邻接点w的下一个邻接点
// 若有返回顶点号,否则返回-1
int nextNeighbor(MGraph G, int v, int w) {
	if (w < G->vexnum) {
		for (int i = w + 1; i < G->vexnum; i++) {
			if (G->Edges[v][i] == 1)
				return i;
		}
		return -1;
	}
	return -1;
}
// 访问该节点的信息
void visit(MGraph G,int v) {
	printf("%c ",G->Vexs[v]);
}
// 广度优先遍历
void BFS(MGraph G, LinkQueue Q, int v) {
	visit(G, v);
	visited[v] = 1;
	enQueue(&Q, v);
	while (!isQueueEmpty(&Q)) {
		deQueue(&Q, &v);
		for (int w = firstNeighbor(G, v); w >= 0; w = nextNeighbor(G, v, w)) {
			if (visited[w] == 0) {
				visit(G, w);
				visited[w] = 1;
				enQueue(&Q, w);
			}
		}
	}
}
// 广度优先遍历 主函数
void BFSTraverse(MGraph G) {
	for (int i = 0; i < G->vexnum; ++i) {
		visited[i] = 0;
	}
	LinkQueue Q;
	initQueue(&Q);
	for (int i = 0; i < G->vexnum; ++i) {
		if (visited[i] == 0)
			BFS(G, Q, i);
	}
}
// 深度优先遍历
void DFS(MGraph G, int v) {
	visit(G, v);
	visited[v] = 1;
	for (int w = firstNeighbor(G, v); w >= 0; w = nextNeighbor(G, v, w)) {
		if (visited[w] == 0)
			DFS(G, w);
	}
}
// 深度优先遍历 主函数
void DFSTraverse(MGraph G) {
	for (int i = 0; i < G->vexnum; ++i) {
		visited[i] = 0;
	}
	for (int i = 0; i < G->vexnum; ++i) {
		if (visited[i] == 0)
			DFS(G, i);
	}
}


int main() {
	MGraph G = initGraph();
	printGraph(G);

	printf("\n广度优先遍历 : ");
	BFSTraverse(G);

	printf("\n深度优先遍历 : ");
	DFSTraverse(G);
	return 0;
}

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

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

相关文章

【区块链】以太坊L2扩容方案与零知识证明

以太坊L2扩容方案与零知识证明 简介 简要概述以太坊L2层现有解决方案简要概述以太坊L2未来扩容的方向简要概述零知识证明的基本概念和零知识证明在以太坊的运用简要概述stark ware的两个产品&#xff0c;但并不推荐现有使用和研究简要概述polygon zkEVM&#xff0c;推荐使用和…

纷享销客斩获“2022-2023年度用户推荐SaaS品牌”

近日&#xff0c;纷享销客受邀出席CSIC2023第八届SaaS应用大会暨云领奖颁奖典礼活动。纷享销客作为国内领先的CRM 云厂商&#xff0c;凭借产品创新以及强大的行业解决方案能力&#xff0c;以绝对实力荣获“2022-2023年度用户推荐SaaS品牌”。 据悉&#xff0c; C SIC云领奖自 …

【SQL武林秘籍】玩转表及其数据

目录 &#x1f4d6;前言 &#x1f600;数据库约束类型 1️⃣not null 非空约束 2️⃣unique 唯一约束 3️⃣default 默认值约束 4️⃣primary key 主键约束 5️⃣foreign key 外键约束 6️⃣check 限制约束 &#x1f609;新增(insert select) &#x1f604;查询(进…

Podman 是 Docker 的直接替代品吗?

在许多地方&#xff0c;你可以读到Podman是Docker的替代品。但是&#xff0c;真的像听起来那么简单吗&#xff1f;在这篇文章中&#xff0c;你将从一个可以立即投入生产使用的Dockerfile开始&#xff0c;并执行Podman命令&#xff0c;就像你使用Docker时会做的那样。让我们看看…

【Vite环境变量】import.meta.env 和 loadEnv使用和区别

前言 我们在做项目时需要各种配置信息&#xff08;如应用标题、API 地址等&#xff09;&#xff0c;这些配置信息可能在不同环境下有所不同&#xff08;如开发环境和生产环境&#xff09;。 如果每次更改开发或者更改生产环境需要修改源代码中的相关配置&#xff0c;这会导致…

老生常谈:接口幂等性,防止并发插入重复数据

分布式系统中&#xff0c;接口幂等性问题&#xff0c;对于开发人员来说&#xff0c;是一个跟语言无关的公共问题。不知道你有没有遇到过这些场景&#xff1a; 有时我们在填写某些form表单时&#xff0c;保存按钮不小心快速点了两次&#xff0c;表中竟然产生了两条重复的数据&a…

AI时代的三类人:探索掌握AIGC,引领未来的人才之路

&#xff08;本文阅读时间&#xff1a;6 分钟&#xff09; 1 AI时代&#xff1a;ChatGPT引领AIGC技术革命 对于那些热衷于探索新技术的小伙伴而言&#xff0c;ChatGPT早已超越了抽象的概念&#xff0c;我们对其能力已有所了解。那么&#xff0c;ChatGPT究竟能够做些什么呢&…

Java 集合全教程

一、集合简介 集合&#xff08;有时称为容器&#xff09;只是将多个元素分组到单个单元中的对象。集合用于存储、检索、操作和传达聚合数据。通常&#xff0c;它们表示形成自然组的数据项&#xff0c;例如扑克手&#xff08;纸牌集合&#xff09;、邮件文件夹&#xff08;字母…

【Java多线程进阶】synchronized工作原理

前言 本期讲解 synchronized 工作的原理以及常见的锁优化机制&#xff0c;相信大家在看完这篇博文后对 synchronized 工作流程有一定的理解。话不多说&#xff0c;让我们快速进入学习吧~ 目录 1. 锁的工作流程 2. 偏向锁 3. 轻量级锁和重量级锁 3.1 轻量级锁 3.2 重量级锁…

Kubernetes基本存储

Kubernetes基本存储 容器的生命周期可能很短&#xff0c;会被频繁地创建和销毁&#xff0c;容器销毁时&#xff0c;保存在容器中的数据也会被清除。为了持久化保存容器中数据&#xff0c;引入Volume概念。 Volume时Pod中多个容器共同访问的共享目录&#xff0c;它被定义在Pod中…

“碳”寻青蓝锦色,锦江酒店(中国区)用行动点亮酒店可持续发展

第52个世界环境日之际&#xff0c;为响应“减塑捡塑”号召&#xff0c;锦江酒店&#xff08;中国区&#xff09;以“‘碳’寻青蓝锦色”为主题&#xff0c;在6月5日至6月11日期间&#xff0c;开启第二届“绿色生活创益周”&#xff0c;通过线上线下联动&#xff0c;倡导时尚低碳…

2023智源大会议程公开丨类脑计算论坛

6月9日&#xff0c;2023北京智源大会&#xff0c;将邀请这一领域的探索者、实践者、以及关心智能科学的每个人&#xff0c;共同拉开未来舞台的帷幕&#xff0c;你准备好了吗&#xff1f;与会知名嘉宾包括&#xff0c;图灵奖得主Yann LeCun、图灵奖得主Geoffrey Hinton、OpenAI创…

Mocha AE:Clip 模块

Clip&#xff08;剪辑&#xff09;模块主要用于对素材或遮罩文件的格式进行查看或设置&#xff0c;包括视频格式、时间码格式、色彩空间等&#xff0c;还可以进行裁剪。 General 常规 Input 输入 当前图层素材。 Matte for Layer n 图层 n 的遮罩 Name 名称 显示素材的名称。 …

通信大史记:互联网的起源故事

点击文末“阅读原文”即可参与节目互动 剪辑、音频 / 卷圈 运营 / SandLiu 卷圈 监制 / 姝琦 文案 / 朱峰 产品统筹 / bobo 这是一期“两年更”的节目&#xff0c;通信大史记录完第一期后&#xff0c;因为资料准备和主播时间的关系一直没有后续更新。今天&#xff0c;我们…

nodejs的path模块

path路径模块 path模块是Node.js官方提供的&#xff0c;用来处理路径的模块。提供一系列的方法和属性&#xff0c;用来满足用户对路径的处理需求。 例如&#xff1a; 如果在js代码中&#xff0c;使用path模块来处理路径&#xff0c;需要先导入 const pathrequire(path)常用的…

chatgpt赋能python:Python如何调成白色的SEO文章

Python 如何调成白色的 SEO 文章 介绍 Python 是一种流行的编程语言&#xff0c;在数据分析、人工智能、Web 开发以及其他许多领域都有广泛的应用。然而&#xff0c;很少有人会把 Python 与 SEO 联系起来。 事实上&#xff0c;Python 是一个强大的工具&#xff0c;可以帮助 …

软件工程师,学会封装不香么

什么是封装 从面向对象编程的角度来说&#xff0c;封装是指利用抽象数据类型将数据和基于数据的操作封装在一起&#xff0c;使其构成一个不可分割的独立实体&#xff0c;数据被保护在抽象数据类型的内部&#xff0c;尽可能地隐藏内部的细节&#xff0c;只保留一些对外接口使之与…

rk3568 TF卡启动

rk3568 SD卡启动 SD卡启动系统&#xff0c;它可以让rk3568在没有硬盘或其他存储设备的情况下启动和运行操作系统。这使得rk3568变得与树梅派一样灵活切换系统&#xff0c;与此同时进行故障排查和修复&#xff0c;而不需要拆卸设备或者使用专业的烧录工具。SD卡启动还可以方便地…

音视频同步的方法:深入探索基于FFmpeg的音视频同步策略

音视频同步艺术&#xff1a;深入探索基于FFmpeg的同步策略 &#xff08;一&#xff09;音视频同步的基本概念与重要性&#xff08;Basic Concepts and Importance of Audio-Video Synchronization&#xff09;1.1 音视频同步的定义与影响&#xff08;Definition and Impact of …

【SQL】Oracle数据库实现远程访问

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 前言 Oracle&#xff0c;是甲骨文公司的一款关系…