【高阶数据结构】图详解第二篇:图的遍历(广度优先+深度优先)

news2024/11/15 13:33:09

文章目录

  • 图的遍历
    • 1. 图的广度优先遍历(一石激起千层浪)
      • 思路分析
      • 代码实现
      • 测试
      • 美团2020校招笔试题:六度人脉
    • 2. 图的深度优先遍历(一条道走到黑)
      • 思路分析
      • 代码实现
      • 测试
    • 3. 对于非连通图情况的处理
    • 4.源码
      • BFS
      • DFS

图的遍历

所谓图的遍历:

即从图中的任一顶点出发,对图中的所有顶点访问一次且只访问一次。
给定一个图G和其中任意一个顶点v0,从v0出发,沿着图中各边访问图中的所有顶点,且每个顶点仅被遍历一次。

ps:

我们后面讲解这些图相关的算法默认都针对邻接矩阵结构的图去讲解,因为后面有些算法针对的图一般都是比较稠密的图,前面我们说了邻接矩阵更适合稠密图。

那具体要如何对一个图进行遍历呢?有哪些方法呢?

1. 图的广度优先遍历(一石激起千层浪)

什么是广度优先遍历呢?

在这里插入图片描述
其实我们之前学过的二叉树的层序遍历就是一种广度优先遍历,要借助一个队列来搞,下面对图的广度优先遍历也是一样

思路分析

那图的广度优先遍历是怎么样的呢?举个栗子:

在这里插入图片描述
这就是对一个图(无向图)的广度优先遍历,红色的数字就是结点遍历的顺序。
大家看,其实就是一层一层的遍历嘛,这里是从A这个顶点开始,所以先遍历结点A,然后依次遍历与A直接相连的一层的结点,接着逐层向外扩展,直到遍历完所有可达的节点。

那我们用代码要怎么实现呢?

其实就跟二叉树的层序遍历类似,借助一个队列就可以搞定。
比如就以这个图为例:
在这里插入图片描述
从顶点A开始,那就让A先入队列。
在这里插入图片描述
栈不为空,出队头元素A,然后我们打印一下A的值,那这个顶点就遍历过去了,然后把与A直接相连的顶点BCD入队列
在这里插入图片描述
栈不为空,继续出队头元素B,然后把与B直接相连的顶点入队列

那现在就出现了一个问题:

现在要把与B直接相连的顶点入队列,而与B直接相连的顶点是A、C、E。
但是,A前面已经遍历过了,C现在也在队列里面呢!
E入队列的话是没问题的,他就是下一层的,但是AC要是再入队列的话这就不对了啊。

那我们如何解决这个问题呢?如果避免某些顶点会重复入队列呢?

🆗,我们可以想办法对已经遍历过的结点做一个标记,这个即使后面再遇到,我们也能分辨出来,不让它再入队列了。

那如何标记已经遍历过的顶点呢?

每个顶点不是都映射一个下标嘛。
所以我们就可以开一个数组,默认都给false,遍历一个结点,就把对应下标位置的值改为true,表示这个结点已经被遍历过了。

那我们何时去标记一个顶点呢?打印之后标记还是入队列就标记呢?

如果打印之后标记的话,B出队列之后其实还会把C带到队列里面
在这里插入图片描述
因为B出队列,然后打印B,此时A已经打印过了被标记了,但是C还没有出队列打印,所以C还没有被标记,所以B打印之后入与B直接相连的顶点ACE的时候,A不会入,但是C还会入。
不管也没关系,因为出到第二个C的时候,第一个C 肯定被标记了,那我们就可以判断,然后不打印第二个C,只把它出队列。

当然更好的做法是这样的:

一个顶点入队列的时候我们就去标记它,这样就不会出现上面的情况(队列里面出现重复顶点)

代码实现

那我们来写一下代码:

在这里插入图片描述
调用的时候给一个起始顶点就可以了
那我们需要一个队列和一个标记数组,首先起始顶点入队列
在这里插入图片描述
然后我们就按照上面的逻辑一层一层走就行了,循环出队列里面的元素并把与之直接相连的结点入队列,直到队列为空就是遍历完了,循环结束
在这里插入图片描述

🆗,就写好了

测试

那我们来测试一下:

在这里插入图片描述
它对应的图应该是这样的
在这里插入图片描述
我们现在以张三为起点对其进行广度优先遍历
在这里插入图片描述
我们来看下结果对不对
在这里插入图片描述
没问题

美团2020校招笔试题:六度人脉

然后我们来看一道美团曾经考过的一道问答题:
题目链接: link
在这里插入图片描述
大家自己先读一下题

🆗,我们来分析一下:

这道题目最终的问题是让我们为小点推荐6度好友,如何找到哪些是它的6度好友呢?
其实很简单:
如果我们用上面实现的广度优先遍历去遍历对应的好友的关系图的话,我们一层一层的打印,第六层打印出来的人就是6度好友

那我们上面实现的广度优先遍历打印的时候并没有分层打印,所以我们可以给上面的代码优化一下,让它分层打印就行了:

那如何做到分层打印呢?
其实这个问题我们之前也有遇到过,前面的文章里我们有讲过一个OJ题,也是二叉树的层序遍历,但是要求把每一层的结点都分开存放到一个数组里面,最终返回的就是一个二维数组嘛。
大家回想一下我们当时怎么做的?
🆗,增加一个变量,去记录每一层结点的个数,每出一个,就- -一次,一层出完,继续记录下一层的个数,这样就可以控制分层打印了。
那我们来把上面的代码改进一下
在这里插入图片描述

试一下:

在这里插入图片描述
就实现分层了

2. 图的深度优先遍历(一条道走到黑)

那深度优先遍历又是什么呢?

在这里插入图片描述
其实我们之前学的二叉树的前序遍历就是一个深度优先遍历,就是先往深了去走,直到走不通了,再往回回溯。
在这里插入图片描述

思路分析

那图的深度优先遍历我们来看一下:

在这里插入图片描述
大家看这个图。
起点是A,那就从A开始,A遍历完,找一个与它直接相连的且没被遍历过的(所以我们这里还要标记遍历过的结点),那我们这里用的图结构是邻接矩阵的话,他找相连顶点的时候肯定就是按照那个结点对应的下标从小到大去找嘛。
那A找到的是B,然后B再去找一个与它邻接的顶点遍历(广度的话B遍历完就是继续找其它与A邻接的遍历),那B找到了C,那后面同样的,C再去找,接着…
一直走,一直走;
直到找到某一个顶点它的所有邻接顶点都被遍历过了,然后往回退,再去走其它没有走过的路径去遍历

所以我们还是用递归来搞就很简单嘛(先递推,再回归),DFS一般都是用递归来实现的。

代码实现

我们来写一下代码:

在这里插入图片描述
那这里递归的话我们可以在搞一个子函数,这基本上算固定套路了
在这里插入图片描述
那这里递归的时候我们传两个参数,首先是本次递归的起点下标(因为底层是邻接矩阵嘛),然后第二个是标记数组,这个要一直往下传,并且上一层递归的改变要延续到下一层,所以要传引用。不传这个的话你就搞一个全局的标记数组。
那递归里面的逻辑呢,很简单:
先访问当前顶点,然后找一个没访问过的邻接顶点,继续递归往深走,一直走到走不动了自动就向上返回了
在这里插入图片描述

就写好了

测试

我们来测试一下:

就还用上面那个图吧
在这里插入图片描述
运行一下
在这里插入图片描述
是没问题的,大家可以自己验证一下
在这里插入图片描述

3. 对于非连通图情况的处理

上一篇文章我们有讲过连通图的概念:

那其实我们上面演示的例子,都是用的连通图。
对于连通图,不论是广度优先遍历还是深度优先遍历,我们上面的代码肯定都是没问题的,肯定能遍历完所有的顶点;

但是如果给我们的图是一个非连通的图呢?比如:

在这里插入图片描述
这样的。
那我们上面的代码针对这种情况会出现什么问题?
对于这种非连通图,如果我们再用上面的代码进行遍历的话,任取一个结点作为起点,比如还是A
那最终的结果就是我们只能遍历到上面的6个结点,最终遍历结束,还剩下面的3个结点我们是遍历不到的,因为它们跟上面的不连通,根本走不下来。

那我们如何解决这个问题呢?

其实也好办:
可再在搞一个循环,每遍历一次之后,我们就去那个标记数组里面看还有没有没被遍历到的顶点,如果有的话,就再取一个没被遍历到的点作为起点,再进行对应的DFS/BFS遍历。
直到标记数组里面所有的位置都变成true,那就证明所有的顶点都被遍历过了。
至此,图的遍历真正结束!

这样就可以解决非连通图的遍历问题!

4.源码

BFS

void BFS(const V& src)
		{
			size_t srci = GetVertexIndex(src);
			//队列
			queue<int> q;
			//标记数组
			vector<bool> visited(_vertexs.size(), false);

			q.push(srci);
			visited[srci] = true;
			int levelSize = 1;

			while (!q.empty())
			{
				//每次循环打印一层
				while (levelSize--)
				{
					//取对头顶点front并打印
					int front = q.front();
					q.pop();
					cout << front << ":" << _vertexs[front] << " ";

					//把front顶点的邻接顶点入队列
					for (size_t i = 0; i < _vertexs.size(); i++)
					{
						//过滤掉已经入过队列的
						if (_matrix[front][i] != MAX_W && visited[i] == false)
						{
							q.push(i);
							visited[i] = true;
						}
					}
				}
				cout << endl;
				//更新levelSize为下一层的结点个数
				levelSize = q.size();
			}
			cout << endl;
		}

DFS

void _DFS(size_t srci, vector<bool>& visited)
		{
			//遍历当前结点并标记
			cout << srci << ":" << _vertexs[srci] << endl;
			visited[srci] = true;

			//寻找当前结点没被访问过的邻接顶点,继续递归往深度遍历
			for (size_t i = 0; i < _vertexs.size(); i++)
			{
				if (_matrix[srci][i] != MAX_W && visited[i] == false)
				{
					_DFS(i, visited);
				}
			}
		}
		void DFS(const V& src)
		{
			size_t srci = GetVertexIndex(src);
			//标记数组
			vector<bool> visited(_vertexs.size(), false);

			_DFS(srci, visited);
		}

图结构我们用的是邻接矩阵,需要代码大家可以去上一篇文章获取…

在这里插入图片描述

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

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

相关文章

uboot启动流程-run_main_loop 到 cmd_process处理说明一

一. uboot启动 uboot命令模式&#xff1a;uboot 启动以后会进入 3 秒倒计时&#xff0c;如果在 3 秒倒计时结束之前按下按下回车键&#xff0c;那么就会进入 uboot 的命令模式。 如果在 uboot 倒计时结束以后都没有按下回车键&#xff0c;就会自动启动 Linux 内 核 …

C++结构体(struct)、结构体和函数、结构体指针

前言 在本文中&#xff0c;您将学习C 编程中的结构。它是什么&#xff0c;如何定义它并在程序中使用它。结构是一个单一名称下不同数据类型的变量的集合。它与类相似&#xff0c;两者都保存着不同数据类型的数据集合。 问题 例如&#xff1a;您要存储有关某个人的一些…

《向量数据库指南》——向量数据库 大模型的“海马体”

在大模型的高调火热之下,向量数据库也获得了前所未有的关注。 近两个月内,向量数据库迎来融资潮,Qdrant、Chroma、Weaviate先后获得融资,Pinecone宣布1亿美元B轮融资,估值达到7.5亿美元。 东北证券预测,到2030年,全球向量数据库市场规模有望达到500亿美元,国内向量数…

【LeetCode热题100】--155.最小栈

155.最小栈 设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元…

dubbo协议与triple协议的对比

分别使用dubbo协议和triple协议&#xff0c;按照官方文档搭建Demo。 两个流程对比下来发现&#xff0c;dubbo协议搭建起来比较简单直接&#xff0c;定义好接口&#xff0c;实现类&#xff0c;然后启动provider和consumer就完事了。而triple协议则需要先定义proto文件 然后增加…

(StackOverflow问答)使用Huggingface Transformers从磁盘加载预训练模型

这是在Stack Overflow上的一个问答&#xff0c;链接如下&#xff1a; Load a pre-trained model from disk with Huggingface Transformers - Stack Overflowhttps://stackoverflow.com/questions/64001128/load-a-pre-trained-model-from-disk-with-huggingface-transformers…

找不到vcomp140.dll,无法继续执行代码?别担心,解决方法在这里!

找不到vcomp140.dll,无法继续执行代码&#xff1f;这个问题很难解决么&#xff1f;在网上看到很多人在咨询这个问题&#xff0c;看来是很多人都遇到了缺失了vcomp140.dll文件的问题啊&#xff0c;小编觉得很有必要来给大家详细的科普一下vcomp140.dll文件&#xff0c;给大家介绍…

测试老鸟整理,Fiddle抓包实战-App数据包抓取,看这一篇就够了...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 我们在做接口测试…

在线剪辑音频教程,从零开始,轻松上手

“在线怎么剪辑音频呀&#xff1f;最近参加了一个线上的歌手大赛&#xff0c;好不容易过了初赛&#xff0c;复赛要求我们准备一首流行歌曲&#xff0c;可是我的音频出了问题&#xff0c;需要进行剪辑&#xff0c;但是我不会进行操作&#xff0c;想求求大家帮帮忙。” 在这个数…

不容易解的题10.7

885.螺旋矩阵III 885. 螺旋矩阵 III - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/spiral-matrix-iii/?envTypelist&envIdZCa7r67M模拟题的一种&#xff0c;说难也难&#xff0c;说简单也简单。模拟题有很多套路题&#xff0c;它们的题解差不多&…

Vue 中 KeepAlive 内置缓存使用

KeepAlive 介绍及使用场景 KeepAlive 是 vue 中的内置组件&#xff0c;当多个组件动态切换时可以对实例状态进行缓存&#xff0c;用法如下 <router-view v-slot"{ Component }"><keep-alive><component :is"Component" /></keep-al…

Adobe Photoshop2018-2024mac/win合集:轻松掌控设计世界

Adobe Photoshop&#xff0c;简称“PS”&#xff0c;是由Adobe Systems公司开发和发行的图像处理软件。Adobe Photoshop主要处理以像素所构成的数字图像。使用其众多的编修与绘图工具&#xff0c;可以有效地进行图片编辑工作。 Adobe Photoshop在图像、图形、文字、视频、出版等…

京东商品数据:8月京东环境电器行业数据分析

8月份&#xff0c;环境电器大盘市场整体下滑。鲸参谋数据显示&#xff0c;8月京东平台环境电器的大盘将近570万&#xff0c;环比下滑约29%&#xff0c;同比下滑约10%&#xff1b;销售额为25亿&#xff0c;环比下滑约23%&#xff0c;同比下滑约8%。 *数据源于鲸参谋-行业趋势分析…

数字经济和法治背景下国企合规数字化转型思考

近年来&#xff0c;数字经济的快速发展已经深刻影响了各行各业的运营方式和商业模式。在这个数字化时代&#xff0c;企业要想保持竞争力和可持续发展&#xff0c;必须紧跟时代潮流&#xff0c;进行数字化转型。而对于国有企业来说&#xff0c;数字化转型中的合规问题显得尤为重…

景联文科技:AI大模型强势赋能,助力自动驾驶迭代升级

我国一直以来都将自动驾驶作为新兴产业发展的重点领域之一&#xff0c;工信部等相关部委出台了一系列自动驾驶发展战略、规划和标准&#xff0c;一些地方政府也在积极开展关于自动驾驶的地方立法&#xff0c;为自动驾驶技术的研发和应用提供更加具体的法律保障。例如&#xff0…

记录:Unity脚本的编写3.0

目录 前言前置控制方法查看效果移动方式 前言 前面记录了一些通过脚本控制对象模型移动和通过用户的操作对模型进行变化的方法&#xff0c;那么为了让我们创造的不论是地形还是模型都拥有真实的物理引擎&#xff08;大雾&#xff09;&#xff0c;那么这次就使用脚本控制模型感…

purr map walk 学习教程 完整版

Function reference • purrrhttps://purrr.tidyverse.org/reference/index.htmlMap over multiple input simultaneously (in "parallel") — pmap • purrr

linux python 保存图形savefig import matplotlib.pyplot as plt

import matplotlib.pyplot as plt # 绘制图形 mod.plot_history(20)# 保存图形 plt.savefig("my_training_ephoes_plot.png") # 保存为PNG格式 # 保存图形并设置dpi参数 plt.savefig("my_plot.png", dpi600) # 保存为PNG格式&#xff0c;设置dpi为300

Conv1d与Conv2d函数用法

Conv1d 和 Conv2d 分别是卷积神经网络&#xff08;CNN&#xff09;中的两种卷积层操作&#xff0c;它们在处理不同维度的数据上有联系和区别&#xff0c;本文是一篇学习笔记。 本文主要包括以下内容&#xff1a; 1.联系2.区别3.Conv1d卷积4.Conv2d卷积5.图解Conv1d卷积&#x…

一文告知HTTP GET是否可以有请求体

HTTP GET是否可以有请求体 先说结论&#xff1a; HTTP协议没有规定GET请求不能携带请求体&#xff0c;但是部分浏览器会不支持&#xff0c;因此不建议GET请求携带请求体。 HTTP 协议没有为 GET 请求的 body 赋予语义&#xff0c;也就是即不要求也不禁止 GET 请求带 body。大多数…