图的广度优先遍历和深度优先遍历

news2024/12/26 22:08:04

前言:在上一篇博客我们学习了图的基本操作,包括图的建立、结点插入与删除等操作,怎么判断我们建立的图是否正确,很简单把它输出出来就是,但是如何输出它,这就是图的遍历问题了。

一.图的遍历

图的遍历是指从图中的某一顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点访问一次且仅访问一次。注意到树是一种特殊的图,所以树的遍历实际上也可视为一种特殊的图的遍历。图的遍历算法是求解图的连通性问题、拓扑排序和求关键路径等算法的基础。
图的遍历比树的遍历要复杂得多,因为图的任意一个顶点都可能和其余的顶点相邻接,所以在访问某个顶点后,可能沿着某条路径搜索又回到该顶点上。为避免同- .顶点被访问多次,在遍历图的过程中,必须记下每个已访问过的顶点,为此可以设一个辅助数组visited[]来标记项点是否被访问过。图的遍历算法主要有两种:广度优先搜索和深度优先搜索

二.广度优先搜索(BFS)

1.基本原理

广度优先搜索( Breadth-First-Search, BFS)类似于二叉树的层序遍历算法。基本思想是:首先访问起始顶点v,接着由v出发,依次访问v的各个未访问过的邻接顶点w1, w2…,wn,然后依次访问w1, w2…, wn;的所有未被访问过的邻接顶点;再从这些访问过的顶点出发,访问它们所有未被访问过的邻接顶点,直至图中所有顶点都被访问过为止。若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作为始点,重复上述过程,直至图中所有顶点都被访问到为止。Dijkstra单源最短路径算法和Prim最小生成树算法也应用了类似的思想。换句话说,广度优先搜索遍历图的过程是以v为起始点,由近至远依次访问和v有路径相通且路径长度为1,2,… 的顶点。广度优先搜索是-种分层的查找过程,每向前走一步可能访问-批顶点,不像深度优先搜索那样有往回退的情况,因此它不是一个递归的算法。为了实现逐层的访问,算法必须借助一 个辅助队列,以记忆正在访问的顶点的下一层顶点。

2.举例说明

K4CT.jpg

假设从a结点开始访问,a先入队。此时队列非空,取出队头元素a,由于b,c与a邻接且未被访问过,于是依次访问b,c,并将b,c依次入队。队列非空,取出队头元素b,依次访问与b邻接且未被访问的顶点d,e,并将d,e入队(注意: a与b也邻接,但a已置访问标记,故不再重复访问)。此时队列非空,取出队头元素c,访问与c邻接且未被访问的顶点f,g,并将f,g入队。此时,取出队头元素d,但与d邻接且未被访问的顶点为空,故不进行任何操作。继续取出队头元素e,将h入队列…最终取出队头元素h后,队列为空,从而循环自动跳出。遍历结果为abcdefgh。

三.深度优先收索(DFS)

1.基本原理

与广度优先搜索不同,深度优先搜索(Depth-First-Search, DFS)类似于树的先序遍历。如其名称中所暗含的意思-样,这种搜索算法所遵循的搜索策略是尽可能“深”地搜索一个图。它的基本思想如下:首先访问图中某- -起始顶点v,然后由v出发,访问与v邻接且未被访问的任意一个顶点w,再访问与wi邻接且未被访问的任意-一个 顶点…重复上述过程。当不能再继续向下访问时,依次退回到最近被访问的顶点,若它还有邻接顶点未被访问过,则从该点开始继续上述搜索过程,直至图中所有顶点均被访问过为止。

2.举例说明

以BFS例子的无向图为例,深度优先搜索的过程:首先访问a,并置a访问标记;然后访问与a邻接且未被访问的顶点b,置b访问标记;然后访问与b邻接且未被访问的顶点d,置d访问标记。此时d已没有未被访问过的邻接点,故返回上一个访问过的顶点b,访问与其邻接且未被访问的顶点e,置e .标…以此类推,直至图中所有的顶点都被访问一次。遍历结果为abdehcfg。

四.核心功能实现

1.BFS函数

void BFS(Graph *G,char x){
	int n;              
	for(int i=0;i<G->vertex_num;i++){
		if(G->vertex[i]==x) {
			n=i;
			break;
		}
	}
	if(n == -1){  
		printf("Error: 结点不存在\n");
	}
	
	char queue[MAX_VERTEX_NUM], front = -1, rear = -1;          
	queue[++rear] = G->vertex[n];      
	visited[n] = true;                 
	while(front !=rear){        
		char current_vertex=queue[++front];      
		printf("%c ",current_vertex);
		
		for(int w=FirstNeighbor(G,current_vertex);w>=0;w=NextNeighbor(G,current_vertex,G->vertex[w])){    
			if(!visited[w]){
				queue[++rear]=G->vertex[w];
				visited[w]=true;    
			}
		}
	}   
}

2.DFS函数

//DFS函数
void DFS(Graph *G,char x){
	int n;              
	for(int i=0;i<G->vertex_num;i++){
		if(G->vertex[i]==x) {
			n=i;
			break;
		}
	}
	if(n == -1){  
		printf("Error: 结点不存在\n");
	}
	printf("%c ",G->vertex[n]);       //访问顶点
	visited2[n]=true;            
	for(int w=FirstNeighbor(G,G->vertex[n]);w>=0;w=NextNeighbor(G,G->vertex[n],G->vertex[w]))
		if(!visited2[w]){
			DFS(G,G->vertex[w]);
		}
}
//DFS相比BFS函数,它使用的是栈也就是递归操作

3.效率分析

BFS(广度优先搜索)和DFS(深度优先搜索)是两种不同的图遍历算法,它们的时间和空间复杂度分析也有所不同。

对于BFS算法,其时间复杂度为O(|V|+|E|),其中|V|表示图中节点的数量,|E|表示边的数量。在BFS中,我们访问每个节点一次,并且遍历了所有与该节点相邻的边,因此时间复杂度为O(|V|+|E|)。对于空间复杂度,BFS需要使用一个队列来存储待访问的节点,因此空间复杂度为O(|V|),其中最坏情况下所有节点都在队列中。因此,当图稠密时,即|E| ~ |V|^2时,BFS的空间复杂度可能会很高。

对于DFS算法,其时间复杂度与图的连接性质有关,最坏情况下可以达到O(|V|+|E|)。然而,实际应用中,DFS的时间复杂度通常比BFS低。对于空间复杂度,DFS需要使用一个栈来存储待访问的节点,因此在最坏情况下,空间复杂度为O(|V|),其中最坏情况下整个图都是一条链。因此,当图非常深时,DFS的空间复杂度可能会很高。

总体而言,BFS和DFS的时间复杂度都是O(|V|+|E|),其中|V|表示图中节点数量,|E|表示边数量。它们的主要区别在于空间复杂度上,BFS需要使用队列存储待访问的节点,空间复杂度为O(|V|),而DFS需要使用栈存储待访问的节点,空间复杂度为O(|V|)。因此,如果空间复杂度很重要,可以选择DFS;如果需要找到最短路径等问题,可以选择BFS。

五.完整代码

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

#define MAX_VERTEX_NUM 20  // 最大顶点数

typedef struct {
	char vertex[MAX_VERTEX_NUM];  // 存放顶点信息
	int edge[MAX_VERTEX_NUM][MAX_VERTEX_NUM];  // 存放边信息
	int vertex_num;  // 顶点数
	int edge_num;  // 边数
} Graph;

bool visited1[MAX_VERTEX_NUM];             //BFS访问标记数组
bool visited2[MAX_VERTEX_NUM];             //DFS访问标记数组

//求图G中顶点x的第一个邻接点下标
int FirstNeighbor(Graph *G,char x){
	int n = -1;      //待查询结点在数组里的下标
	for(int i=0;i<G->vertex_num;i++){
		if(G->vertex[i]==x) {
			n=i;
			break;
		}
	}
	if(n == -1){  // 如果查询节点不存在
		printf("Error: 结点不存在\n");
	}
	int count=0;
	int nx;        //第一个邻接点在数组里的下标
	//现在知道了他在第n个,所以我们查询边矩阵第n行中第一个1在哪就知道第一个邻接点是谁
	for(int j=0;j<G->vertex_num;j++){
		if(G->edge[n][j]==1) count++;
		if(count==1){
			nx=j;
			break;
		}
	}
	return nx;
}

int NextNeighbor(Graph *G,char x,char y){
	int n = -1;      //待查询结点在数组里的下标
	for(int i=0;i<G->vertex_num;i++){
		if(G->vertex[i]==x) {
			n=i;
			break;
		}
	}
	if(n == -1){  // 如果查询节点不存在
		printf("Error: 结点不存在\n");
	}
	int count=0;
	int ny = -1;   //存放返回邻接点数组下标
	for(int j=0;j<G->vertex_num;j++){
		if(G->vertex[j]==y) {
			ny=j;
			break;
		}
	}
	int nx = -1;   //存放返回邻接点数组下标
	for(int j=ny+1;j<G->vertex_num;j++){
		if(G->edge[n][j]==1){
			nx=j;
			break;
		}   
	}
	return nx;
}

void BFS(Graph *G,char x){
	int n;              
	for(int i=0;i<G->vertex_num;i++){
		if(G->vertex[i]==x) {
			n=i;
			break;
		}
	}
	if(n == -1){  
		printf("Error: 结点不存在\n");
	}
	
	char queue[MAX_VERTEX_NUM], front = -1, rear = -1;          
	queue[++rear] = G->vertex[n];      
	visited1[n] = true;                 
	while(front !=rear){        
		char current_vertex=queue[++front];      
		printf("%c ",current_vertex);
		
		for(int w=FirstNeighbor(G,current_vertex);w>=0;w=NextNeighbor(G,current_vertex,G->vertex[w])){    
			if(!visited1[w]){
				queue[++rear]=G->vertex[w];
				visited1[w]=true;    
			}
		}
	}   
}
//你以为这就是执行函数吗?如果你这样做存在多个连通分量绝对出错


//BFS执行函数
void BFSTraverse(Graph *G){
	for(int i=0;i<G->vertex_num;i++){
		visited1[i]=false;           //标记数组初始化
	}
	for(int i=0;i<G->vertex_num;++i){
		if(!visited1[i])
			BFS(G,G->vertex[i]);
	}
}

//DFS函数
void DFS(Graph *G,char x){
	int n;              
	for(int i=0;i<G->vertex_num;i++){
		if(G->vertex[i]==x) {
			n=i;
			break;
		}
	}
	if(n == -1){  
		printf("Error: 结点不存在\n");
	}
	printf("%c ",G->vertex[n]);       //访问顶点
	visited2[n]=true;            
	for(int w=FirstNeighbor(G,G->vertex[n]);w>=0;w=NextNeighbor(G,G->vertex[n],G->vertex[w]))
		if(!visited2[w]){
			DFS(G,G->vertex[w]);
		}
}
//DFS相比BFS函数,它使用的是栈也就是递归操作

//DFS执行函数
void DFSTraverse(Graph *G){
	for(int i=0;i<G->vertex_num;i++){
		visited2[i]=false;           //标记数组初始化
	}
	for(int i=0;i<G->vertex_num;++i){
		if(!visited2[i])
			DFS(G,G->vertex[i]);
	}
}

int main(){
	Graph G;
	G.vertex_num=5;
	G.edge_num=6;
	//人为构造图的顶点
	for(int i=0;i<G.vertex_num;i++){
		G.vertex[i]= 'A'+i;
	}
	for(int i=0; i<G.vertex_num; i++){
		for(int j=0; j<G.vertex_num; j++){
			G.edge[i][j]=0;
		}
	}
	//人为构造图的边
	G.edge[0][1]=1;
	G.edge[0][3]=1;
	G.edge[1][2]=1;
	G.edge[2][3]=1;
	G.edge[1][4]=1;
	G.edge[2][4]=1;
	printf("BFS的遍历结果为:");
	BFSTraverse(&G);
	printf("\n");
	printf("DFS的遍历结果为:");
	DFSTraverse(&G);	
	return 0;
}

六.运行结果

KA24.jpg

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

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

相关文章

初识C语言的static关键字(修饰局部变量、全局变量和函数)

目录 学习目标 1.static 修饰局部变量 2.static 修饰全局变量 3.static 修饰函数 学习目标 static修饰局部变量static修饰全局变量static修饰函数 1.static 修饰局部变量 &#xff08;1&#xff09;static修饰局部变量后&#xff0c;这时局部变量就是静态的局部变量。 &am…

光模块安规认证简介

背景 认证是指由认证机构证明产品、服务、管理体系符合相关技术规范的强制性要求或者标准的合格评定活动。其中产品认证是通过对产品的不同层级认证实现各级材料的可追溯性。认证按照内容分类大致包括&#xff1a;安全、电磁兼容&#xff08;EMC&#xff09;和环保等。按照必要…

PyTorch 中通道在最后的内存格式(beta)

PyTorch 中通道在最后的内存格式&#xff08;beta&#xff09; 什么是通道在最后 通道在最后的内存格式是在保留内存尺寸的顺序中对 NCHW 张量进行排序的另一种方法。 通道最后一个张量的排序方式使通道成为最密集的维度&#xff08;又称为每像素存储图像&#xff09;。 例如…

Java——《面试题——SpringCloud》

前文 java——《面试题——基础篇》 Java——《面试题——JVM篇》 Java——《面试题——多线程&并发篇》 Java——《面试题——Spring篇》 Java——《面试题——SpringBoot篇》 Java——《面试题——MySQL篇》​​​​​​ 目录 前文 1、什么是SpringCloud 2、什…

实战:NPMYARN构建工具实践-2023.6.22(测试成功)

实战&#xff1a;NPM&YARN构建工具实践-2023.6.22(测试成功) 目录 推荐文章 https://www.yuque.com/xyy-onlyone/aevhhf?# 《玩转Typora》 实验环境 gitlab/gitlab-ce:15.0.3-ce.0 jenkins/jenkins:2.346.3-2-lts-jdk11 openjdk 11.0.18 [rootDevops6 ~]#npm -v 6.14.12…

智能汽车 | 整车控制器(VCU)系统框图,功能拆解及供应商排名

摘要&#xff1a; 随着新能源EE架构的迭代及控制单元集成度越来越高&#xff0c;VCU的功能可能会被拆解到中央控制器域控制器&#xff0c;或者拆解到多合一的控制单元&#xff1b; VCU&#xff08;Vehicle Control Unit&#xff09;即整车控制器&#xff0c;是新能源汽车控制系…

JavaSE基础语法--static成员

假设我们现在有一个场景&#xff0c;定义一个学生类。 class Student{private String name;private int age;private int classroom_num;public Student(String name, int age, int classroom_num) {this.name name;this.age age;this.classroom_num classroom_num;} } pu…

翻筋斗觅食策略改进灰狼算法(IGWO)

目录 一、动态扰动因子策略 二、翻筋斗觅食策略 三、改进灰狼算法收敛曲线图 灰狼优化算法存在收敛的不合理性等缺陷&#xff0c;目前对GWO算法的收敛性改进方式较少&#xff0c;除此之外&#xff0c;当GWO迭代至后期&#xff0c;所有灰狼个体都逼近狼、狼、狼&#xff0c;…

HarmonyOS学习路之开发篇—多媒体开发(媒体会话管理开发)

一、媒体会话管理开发 AVSession是一套媒体播放控制框架&#xff0c;对媒体服务和界面进行解耦&#xff0c;并提供规范的通信接口&#xff0c;使应用可以自由、高效地在不同的媒体之间完成切换。 约束与限制 在使用完AVSession类后&#xff0c;需要及时进行资源释放。播放器类…

Linux常用命令——ftpshut命令

在线Linux命令查询工具 ftpshut 在指定的时间关闭FTP服务器 补充说明 功能说明&#xff1a;在指定的时间关闭ftp服务器。本指令提供系统管理者在设置的时间关闭FTP服务器&#xff0c;且能在关闭之前发出警告信息通知用户。关闭时间若设置后为"none"&#xff0c;则…

【实战项目开发技术分享】如何解决机器人运动不平稳的问题

文章目录 前言一、机器人设计的考虑因素二、控制算法的优化三、传感器改进四、实时监测与调试五、总结前言 机器人的运动平稳性对于其在各种应用中的成功执行任务至关重要。当机器人在执行任务过程中出现不稳定的运动,可能导致任务失败、损坏周围环境或甚至危及人员安全。因此…

ChatGPT在能源行业的预测场景:智能能源管理和异常检测的未来趋势

第一章&#xff1a;引言 能源是现代社会发展的关键驱动力之一&#xff0c;然而&#xff0c;传统的能源管理方法存在许多挑战&#xff0c;如能源浪费、供需不平衡以及能源异常等。为了应对这些挑战&#xff0c;智能能源管理系统逐渐崭露头角。在本文中&#xff0c;我们将探讨Ch…

基于Java+Swing实现仿QQ屏幕截图工具

基于JavaSwing实现仿QQ屏幕截图工具 一、系统介绍二、功能展示三、其它1.其他系统实现四.获取源码 一、系统介绍 实现能够实现对屏幕的随机截取&#xff0c;复制&#xff0c;保存以及添加文字等操作&#xff0c;便于用户对数据的处理。 该软件的功能&#xff1a; &#xff08…

I/O设备与主机信息传送的方式(程序查询方式,程序中断方式,DMA方式)

一.程序查询方式 CPU和I/O设备串行工作&#xff0c;CPU连接I/O设备和内存&#xff0c;CPU需要等待&#xff0c;效率很低 &#xff08;由CPU通过程序不断查询IO设备是否已经做好准备&#xff0c;从而控制IO设备与主机交换信息&#xff09; 二.程序中断方式&#xff1a; 中断&…

前端Vue自定义数字输入框 带加减按钮的数字输入框组件

前端Vue自定义数字输入框 带加减按钮的数字输入框组件&#xff0c; 下载完整代码请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13163 效果图如下&#xff1a; # cc-numbox #### 使用方法 使用方法 <!-- title: 标题 isSetMax: 是否设置最…

手把手叫你学会搭建FreeRTOS工程文件

手把手教你学会搭建FreeRTOS工程文件 一.序言二.提取文件2.1 Source文件夹2.2 portble文件夹2.3 Demo 文件夹 三.建立FreeRTOS工程3.1 新建FreeRTOS目录3.2 移植src文件夹3.3 移植port文件夹3.4 添加include文件夹3.5 提取FreeRTOSConfig.h文件3.5.1 拷贝FreeRTOSConfig.h文件 …

前端Vue自定义简单实用轮播图封装组件 快速实现轮播图

前端Vue自定义简单实用轮播图封装组件 快速实现轮播图&#xff0c; 下载完整代码请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13153 效果图如下&#xff1a; # cc-mySwiper #### 使用方法 使用方法 <!-- 自定义轮播图 swiperArr: 轮播数…

Day5——数据库基础2-SQL查询语句

网络安全学习笔记Day5 SQL查询语句&#xff08;重在实操&#xff01;&#xff01;&#xff01;&#xff09; 回顾1.中英文符号混淆2.数据库的操作流程&#xff08;回顾mysql相关语句&#xff09;3.“everything”工具 学习目标1.查询数据基本语法形式基本查询语句表单查询where…

ubuntu20下yolov4训练多目标实战

1、安装nvidia驱动和cudnn,不熟悉的小伙伴请移步&#xff1a;Ubuntu20.04安装NVIDIA显卡驱动、CUDA、CUDNN及突破NVENC并发限制_ubuntu20.04安装显卡驱动_BetterJason的博客-CSDN博客 2、编译opencv&#xff0c;不熟悉的小伙伴请移步:ubuntu20.04 和centos8平台opencv4.5.3&am…

【八大排序(九)】计数排序-非比较排序法

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:八大排序专栏⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习排序知识   &#x1f51d;&#x1f51d; 计数排序 1. 前言2. 计数排序基本思路3. …