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

news2025/1/23 4:45:49

上篇博客介绍了图的概念与图的存储(邻接矩阵、邻接表):   接下来就是介绍图的遍历。

图的遍历

        给定一个图G和其中任意一个顶点v0,从v0出发,沿着途中各边访问图中的所有顶点,且每个顶点仅被遍历一次。"遍历"即对结点进行某种操作的意思。

广度优先遍历

遍历原理

比如现在要找东西,假设有三个抽屉,东西在那个抽屉不清楚,现在要将其找到,广度优先遍历的做法是:

1. 先将三个抽屉打开,在最外层找一遍。

2. 将每个抽屉中的红色的盒子打开,再找一遍。

3. 将红色盒子中绿色盒子打开,再找一遍

找完所有的盒子,注意:每个盒子只能找一次,不能重复找。

 第一个顶点入队列,然后再入与A顶点连通的顶点,再出A顶点,入与A顶点连通的顶点——B、C、D,当B出队列时,再入队列与B顶点联通的顶点,即A、C、E,因为A顶点之前已经入过队列了,则出现一个问题:如何防止节点被重复遍历。

则我们实现广度优先遍历要使用一个队列和一个标记结构(vector)。

实现步骤

1. 将顶点放入队列中。

2. 取出队列头部数据,将其标记数组中标记为已访问。

3. 把与该顶点相连通的顶点再放入队列中,在临界矩阵中则是纵向遍历。

4. 直到队列为空

void BFS(const V& src)  //src是遍历的起点
{
	size_t srci = GetVertexIndex(src);
	//队列中存放下标
	queue<int> q;
	q.push(srci);
	//标记位图
	size_t sz = _vertexs.size();
	vector<bool> visited(sz, false);
	//如果不为空,就入队列
	while (!q.empty())
	{
		int front = q.front();
		q.pop();
		cout << front << ":" << _vertexs[front] << endl;
		//标记该结点
		visited[front] = true;
		//将front的邻接顶点入队列
		for (size_t i = 0; i < sz; i++)
		{
			if (_matrix[front][i] != INT_MAX)
			{
				//如果该顶点没有出现过
				if (visited[i] == false)
				{
					q.push(i);
					visited[i] = true;
				}
			}
		}
	}
}

测试代码:

该测试代码的逻辑连通图:

广度优先遍历的应用

这道题目的实际就是让你实现广度优先遍历时实现对层数的控制。

具体实现就是在我们出完一度好友后,能够获取到下一度好友的数量,这个数量其实就是当前队列中剩余的好友的数量。

void BFS_level(const V& src)  //src是遍历的起点
{
	size_t srci = GetVertexIndex(src);
	//队列中存放下标
	queue<int> q;
	q.push(srci);
	//标记位图
	size_t sz = _vertexs.size();
	vector<bool> visited(sz, false);
	int levelsize = q.size();
	//如果不为空,就入队列
	while (!q.empty())
	{
		for (int l = 0; l < levelsize; l++)
		{
			int front = q.front();
			q.pop();
			cout << front << ":" << _vertexs[front] << " ";
			//标记该结点
			visited[front] = true;
			//将front的邻接顶点入队列
			for (size_t i = 0; i < sz; i++)
			{
				if (_matrix[front][i] != INT_MAX)
				{
					//如果该顶点没有出现过
					if (visited[i] == false)
					{
						q.push(i);
						visited[i] = true;
					}
				}
			}
		}
		cout << endl;
		levelsize = q.size();
	}
}

运行结果:

深度优先遍历

遍历原理

如果使用深度优先遍历,那在这三个抽屉中找东西的步骤则是这样的:

1. 先将第一个抽屉打开,在最外层找一遍

2. 将第一个抽屉中的红盒子打开,在红盒子中找一遍

3. 将红盒子中绿盒子打开,在绿盒子中找一边

4. 递归查找剩余的两个盒子

深度优先遍历:将一个抽屉一次性遍历完(包括该抽屉中包含的小盒子),再去递归遍历其他盒子。

实现步骤

1. 取到该顶点,然后调用_DFS函数

2. _DFS函数中打印该顶点,然后将其标记

3. 遍历与该顶点相连通的顶点,在临界矩阵中则是纵向遍历。

4. 如果与其连通的不是顶点有权值,并且未访问过,则调用_DFS.

//深度遍历
void DFS(const V& src)
{
	cout << "DFS:" << endl;
	size_t srci = GetVertexIndex(src);
	vector<bool> visited(_vertexs.size(), false);
	_DFS(srci, visited);
}
void _DFS(size_t srci, vector<bool>& visited)
{
	cout << srci << ": " << _vertexs[srci] << endl;
	//标记该点已访问
	visited[srci] = true;
	//找一个srci相邻并未访问的顶点,去深度遍历
	for (size_t i = 0; i < _vertexs.size(); i++)
	{
		if (_matrix[srci][i] != MAX_W && visited[i] == false)
		{
			_DFS(i, visited);
		}
	}
}

测试:

如果我们传入的是张三,则会遍历张三连通的节点,即李四,李四再遍历连通的节点,因为李四中张三是访问过的,则跳过张三,然后李四发现没有顶点与其连通,则返回。依次过程进行遍历。结果如上图。

非连通图的遍历

如果给定的图不是连通图,以某个点位起点就没有遍历完成。

以下图逻辑为例:

我们需要如果想将其全部遍历,则要配合使用我们的visited数组,遍历visited数组,将其中没有出现过的顶点作为参数再次调用DFS或BFS函数。

以下以DFS举例:

//深度遍历
void DFS(const V& src)
{
	cout << "DFS:" << endl;
	size_t srci = GetVertexIndex(src);
	vector<bool> visited(_vertexs.size(), false);
	_DFS(srci, visited);
	//遍历未连通的顶点
	for (size_t i = 0; i < visited.size(); i++)
	{
		if (visited[i] == false)
		{
			_DFS(i, visited);
		}
	}
}
void _DFS(size_t srci, vector<bool>& visited)
{
	cout << srci << ": " << _vertexs[srci] << endl;
	//标记该点已访问
	visited[srci] = true;
	//找一个srci相邻并未访问的顶点,去深度遍历
	for (size_t i = 0; i < _vertexs.size(); i++)
	{
		if (_matrix[srci][i] != MAX_W && visited[i] == false)
		{
			_DFS(i, visited);
		}
	}
}

遍历结果:

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

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

相关文章

读SQL学习指南(第3版)笔记09_条件逻辑与事务

1. 条件逻辑 1.1. SQL逻辑根据特定列或表达式转向不同的分支来处理 1.2. 在程序执行时从多个路径中选取一个路径的能力 1.3. case表达式 1.3.1. 所有的主流数据库服务器都提供了旨在模拟大多数编程语言中if-then-else 语句的内建函数 1.3.1.1. Oracle的decode()函数 1.3.…

T113-S3-ov5640摄像头调试

目录 前言 一、ov5640模组介绍 1. 图像传感器特性 2. 接口和控制 3. 图像处理能力 4. 应用领域 二、原理图连接 三、设备树配置 四、驱动配置 五、ov5640使用 六、异常记录 总结 前言 摄像头模块是嵌入式系统中常见的外设&#xff0c;用于捕获图像和视频。在本篇文章…

一不留神就掉坑

乘除顺序问题 在据卡特兰数[1]公式,解决leetcode-96 不同的二叉搜索树[2]时,遇到一个非常诡异的问题, package mainimport "fmt"func main() { for i : 0; i < 40; i { fmt.Printf("第%d个卡特兰数为:%d\n", i, numTrees(i)) }}func numTrees(n int) i…

【超详细~KVM】KVM概述、安装及简单操作-------从小白到大神之路之学习运维第91天

第四阶段提升 时 间&#xff1a;2023年8月30日 参加人&#xff1a;全班人员 内 容&#xff1a; KVM概述、安装及简单操作 目录 一、KVM 概述 二、KVM工作原理 三、KVM应用场景 四、centos7 下安装部署 五、新建虚拟机步骤 1、创建存储池并创建存储卷 2、点击号创建…

MPI之通信模式(标准,缓存,同步,就绪)

MPI缓冲区 由MPI自行维护的一块内存区域&#xff0c;也可由用户(MPI_Bsend)自行维护&#xff1b;发送方 维护一块发送缓冲区&#xff1b; 接收方 维护一块接收缓冲区。 数据收发过程&#xff1a; 当发送端将数据拷贝到自身的数据缓冲区后(注意这里是拷贝&#xff0c;即数据到…

Redis——认识Redis

简单介绍 Redis诞生于2009年&#xff0c;全称是Remote Dictionary Server&#xff0c;远程词典服务器&#xff0c;是一个基于内存的键值型NoSQL数据库。 特征 键值&#xff08;Key-value&#xff09;型&#xff0c;value支持多种不同数据结构&#xff0c;功能丰富单线程&…

多线程专栏------多线程的实现方式(三)

目录 1、使用线程池1.1、什么是线程池1.2、使用线程池的优点1.3、线程池的核心工作流程1.3、线程池的五种状态生命周期1.3.1、RUNNING1.3.2、SHUTDOWN1.3.3、STOP1.3.4、TIDYING1.3.5、TERMINATED 1.4、创建线程池的方式1.4.1、通过 ThreadPoolExecutor 创建1.4.1.1、线程池的核…

类的静态成员变量 static member

C自学精简教程 目录(必读) 类的静态成员 static member 变量全局只有一份副本&#xff0c;不会随着类对象的创建而产生副本。 static 静态成员 在类的成员变量前面增加static关键字&#xff0c;表示这个成员变量是类的静态成员变量。 #include <iostream> using name…

kaggle赛后总结

1. 宽表 2.缺失值的处理方法 最简单粗暴的就是删除&#xff0c;这种情况是凡是有缺失值行数很少。均值替代。缺失值的行数比较多一点儿的时候&#xff0c;直接删除会影响样本数量&#xff0c;那就均值替代&#xff0c;或者中位数替代等方法。还有复杂的方法&#xff0c;把有缺…

NMS(非极大值抑制)的 Python 实现

文章目录 1. NMS的步骤2. Python代码 非极大值抑制&#xff08;Non-Maximum Suppression&#xff0c;NMS&#xff09;是一种在目标检测中常用的技术。 NMS的目的是消除重叠区域中冗余的边界框&#xff0c;并选择最具代表性的目标作为最终结果。通过调整重叠阈值&#xff0c;可…

400电话号码怎么开通

开通400电话是企业提供客户服务的重要步骤。下面是一些步骤和注意事项&#xff0c;帮助您顺利开通400电话。 第一步&#xff1a;选择400电话服务提供商 选择一家可靠的400电话服务提供商非常重要。您可以通过搜索引擎、咨询行业内人士或者参考其他企业的经验来选择合适的服务提…

FLASH读写数据

目录 嵌入式 Flash大概了解 数据手册2.3.2章节 结构图f407 等待周期 Flash 控制寄存器解锁 编程/擦除并行位数 擦除 编程&#xff08;写入&#xff09; 工程程序 嵌入式 Flash大概了解 可以从flash区域启动程序&#xff1b;大概是程序区可以在flash&#xff0c;所以是可以…

python调用git出错:ImportError: Failed to initialize: Bad git executable.

报错信息 #报错信息 Traceback (most recent call last): File “”, line 1, in File “C:\Python27\lib\site-packages\git_init_.py”, line 85, in raise ImportError(‘Failed to initialize: {0}’.format(exc)) ImportError: Failed to initialize: Bad git executab…

春秋云镜 CVE-2018-16283

春秋云镜 CVE-2018-16283 WordPress Plugin Wechat Broadcast LFI 靶标介绍 WordPress Plugin Wechat Broadcast LFI 启动场景 漏洞利用 exp # Exploit Title: WordPress Plugin Wechat Broadcast 1.2.0 - Local File Inclusion # Author: Manuel Garcia Cardenas # Date:…

图论-01-图的基本表示-邻接矩阵和邻接表-Java

文章目录 邻接矩阵邻接表邻接表的问题和改进总结 邻接矩阵 import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Scanner;public class AdjMatrix {private int V;private int E;private int[][] adj;// 构造函数&#xff0c;从文…

利用网络流通过拆点判断图的路径存在性问题:abc318_g

https://atcoder.jp/contests/abc318/tasks/abc318_g 对于图上一类路径是否存在问题&#xff0c;可以考虑网络流。 Trick1 路径存在转网络流 题目转化为&#xff1a; 找出两条不交路径 B->A, B->C 对于已经找到的路径&#xff0c;我们不能再走。对于当前我们找到的某条…

Matlab图像处理-幂次变换

幂次变换 如下图所示的幂次变换函数曲线图&#xff1a; 当γ <1时&#xff0c;效果和对数变换相似&#xff0c;放大暗处细节&#xff0c;压缩亮处细节&#xff0c;随着数值减少&#xff0c;效果越强。 当γ >1时&#xff0c;放大亮处细节&#xff0c;压缩暗处细节&…

云备份——配置信息及获取配置信息类模块

一&#xff0c;配置信息 使用文件配置加载一些程序的运行关键信息&#xff0c;如ip&#xff0c;端口等&#xff0c;可以让程序的运行更加灵活 我们需要的配置信息如下 IP地址端口号热点判断时间&#xff0c;也就是非热点文件的时间要求文件下载的URL前缀路径&#xff0c;用于表…

Matlab图像处理-

对数变换 对数变换的一项主要应用是压缩动态范围。一些特别的图像在实际显示中&#xff0c;高灰度值部分较占优势&#xff0c;而低灰度值的可见细节部分丢失。通过计算对数&#xff0c;如10的动态范围会降至14左右[即 ln1013.8]&#xff0c;这样就更易于处理。 对数变换就是压…

Pygame中Trivia游戏解析6-4

3.3.3 显示题目选项 在显示题目选项时&#xff0c;有三种情况&#xff1a;分别是用户还未选择答案时&#xff1b;用户的答案是正确时&#xff1b;用户的答案是错误时。 &#xff08;1&#xff09;用户还未选择答案时 此时&#xff0c;用白色显示四个备选答案&#xff0c;如图…