数据结构 —— 图的遍历

数据结构 —— 图的遍历

  • BFS(广度遍历)
  • 一道美团题
  • DFS(深度遍历)

我们今天来看图的遍历,其实都是之前在二叉树中提过的方法,深度和广度遍历

在这之前,我们先用一个邻接矩阵来表示一个图:

#pragma once
#include<iostream>
#include<vector>
#include<map>
using namespace std;

//图的模板化
namespace matrix
{
	template<class V, class W, W MAX_W, bool Direction = false>
	class Graph
	{
	public:
		Graph(const V* vertex, size_t n)
		{
			//开辟空间
			_vertex.reserve(n);
			for (int i = 0; i < n; i++)
			{
				_vertex.push_back(vertex[i]);
				_index[vertex[i]] = i;
			}

			//初始化矩阵
			_matrix.resize(n);

			for (auto& e : _matrix)
			{
				e.resize(n, MAX_W);
			}
		}

		//寻找图中的点
		size_t FindSrci(const V& vertex)
		{
			auto ret = _index.find(vertex);

			if (ret != _index.end())
			{
				return ret->second;
			}
			else
			{
				throw invalid_argument("不存在的顶点");
				return -1;
			}
		}

		void _AddEdge(size_t srci, size_t desi, W w)
		{
			_matrix[srci][desi] = w;

			if (Direction == false)
			{
				_matrix[desi][srci] = w;
			}
		}

		//加边
		void AddEdge(const V& srci, const V& desi, W w)
		{
			size_t srcIndex = FindSrci(srci);
			size_t desIndex = FindSrci(desi);

			_AddEdge(srcIndex, desIndex, w);
		}

		void Print()
		{
			//打印标题行
			cout << "      ";
			for (int i = 0; i < _vertex.size(); i++)
			{
				cout << _vertex[i] << " ";
			}
			cout << endl;

			//打印矩阵
			for (int i = 0; i < _vertex.size(); i++)
			{
				cout << _vertex[i] << " ";
				for (int j = 0; j < _matrix[i].size(); j++)
				{
					if (_matrix[i][j] != MAX_W)
					{
						printf("%5d", _matrix[i][j]);
					}
					else
					{
						printf("%5c", '#');
					}
				}
				cout << endl;
			}
		}

	private:
		//存放顶点
		vector<V> _vertex;
		//映射关系
		map<V, size_t> _index;
		//矩阵,图的表示
		vector<vector<W>> _matrix;
	};

	void TestGraph()
	{
		Graph<char, int, INT_MAX, true> g("0123", 4);
		g.AddEdge('0', '1', 1);
		g.AddEdge('0', '3', 4);
		g.AddEdge('1', '3', 2);
		g.AddEdge('1', '2', 9);
		g.AddEdge('2', '3', 8);
		g.AddEdge('2', '1', 5);
		g.AddEdge('2', '0', 3);
		g.AddEdge('3', '2', 6);
		g.Print();
	}

	void TestGraph2()
	{
		string a[] = {"海皇","高斯","小傲","小潮","胖迪","小杨","皖皖"};
		Graph<string, int,INT_MAX, false> g1(a, sizeof(a)/sizeof(a[0]));
		g1.AddEdge("小潮", "小傲", 30);
		g1.AddEdge("小潮", "高斯", 83);
		g1.AddEdge("小潮", "海皇", 34);
		g1.AddEdge("胖迪", "海皇", 78);
		g1.AddEdge("胖迪", "小傲", 76);
		g1.AddEdge("小杨", "皖皖", 54);
		g1.AddEdge("小杨", "高斯", 48);

		g1.Print();
	}
}

在这里插入图片描述在这里插入图片描述

BFS(广度遍历)

广度优先遍历和之前二叉树的广度优先遍历一样,我们需要队列来辅助
在这里插入图片描述我们逻辑上是这张图,但是我们实际遍历的时候,我们遍历的是一个矩阵(二维数组)
在这里插入图片描述因为这个图是联通图,我们从任意一个顶点出发都可以遍历完所有顶点,假设我们从小潮开始:
在这里插入图片描述我们仿照二叉树那里的思路,把相连的节点入队列,把小傲,高斯,海皇入队列:
在这里插入图片描述在矩阵上的表现就是把小潮这一行,不为#的数值的结点入队列
在这里插入图片描述好,现在有一个问题,假设我现在遍历到了海皇,海皇和小潮相连,按照上面的规则,我们应该把小潮入队列,但是我们一开始就是从小潮开始的,就会造成重复访问,这该则么办呢?

所以在图这里,我们要引入一个数组,标记我们已经访问过的结点,标记过的结点在之后的访问过程中不再入队

在这里插入图片描述
我们来实现一下:

	void BFS(const V& vertex)
		{
			int vertexIndex = FindSrci(vertex);
			//队列和标记数组
			int n = _vertex.size();
			queue<size_t> q;
			vector<bool> visited(n, false);

			//先入最先访问节点
			q.push(vertexIndex);
			//标记
			visited[vertexIndex] = true;

			while (!q.empty())
			{
				//出队
				size_t front = q.front();
				q.pop();
				cout << _vertex[front] << " ";

				//遍历该行
				for (int i = 0; i < _vertex.size(); i++)
				{
					if (_matrix[front][i] != MAX_W)
					{
						if (visited[i] == false)
						{
							q.push(i);
							visited[i] = true;
						}
					}
				}
				cout << endl;
			}
			
		}

一道美团题

在这里插入图片描述读了题之后,这里让我们计算几度好友,其实我们在BFS已经按照一度二度的顺序打印了,但是我们没有区分,我们区分一下就可以了

void BFS(const V& vertex)
{
    // 获取目标顶点的索引
    int vertexIndex = FindSrci(vertex);
    
    // 初始化队列和标记数组
    int n = _vertex.size();
    queue<size_t> q; // 创建一个队列用于存储待访问的顶点
    vector<bool> visited(n, false); // 创建一个布尔数组,用于标记顶点是否已被访问

    // 将起始顶点添加到队列中,并标记为已访问
    q.push(vertexIndex);
    visited[vertexIndex] = true;

    int levelSize = 1; // 初始化当前层级的顶点数量,初始时只有起始顶点
    int degree = 0; // 初始化度数(即好友的层次)

    // 当队列非空时,继续广度优先搜索
    while (!q.empty())
    {
        // 输出当前度数的好友
        cout << "[" << degree << "]" << "度好友 ";
        
        // 对当前层级的所有顶点进行处理
        for (int j = 0; j < levelSize; j++)
        {
            // 从队列中取出一个顶点
            size_t front = q.front();
            q.pop();
            
            // 输出当前顶点的信息
            cout << _vertex[front] << " ";
            
            // 遍历与当前顶点相邻的所有顶点
            for (int i = 0; i < _vertex.size(); i++)
            {
                // 如果存在一条边连接当前顶点和下一个顶点
                if (_matrix[front][i] != MAX_W)
                {
                    // 如果下一个顶点未被访问过
                    if (!visited[i])
                    {
                        // 将下一个顶点添加到队列中,以便后续访问
                        q.push(i);
                        
                        // 标记下一个顶点为已访问
                        visited[i] = true;
                    }
                }
            }
        }
        
        // 更新下一层级的顶点数量
        levelSize = q.size();
        
        // 换行输出,准备进入下一度数的好友输出
        cout << endl;
        
        // 增加度数计数器
        degree++;
    }
    
    // 最终换行,使输出更清晰
    cout << endl;
}

在这里插入图片描述

DFS(深度遍历)

深度遍历是一条道走到黑:
在这里插入图片描述

// 深度优先搜索的私有辅助函数
void _DFS(size_t vertexIndex, vector<bool>& visited)
{
    // 如果当前顶点尚未访问过
    if (!visited[vertexIndex])
    {
        // 输出当前顶点的值
        cout << _vertex[vertexIndex] << endl;
        
        // 将当前顶点标记为已访问
        visited[vertexIndex] = true;

        // 遍历所有顶点
        for (size_t i = 0; i < _vertex.size(); i++)
        {
            // 如果当前顶点和遍历到的下一个顶点之间存在边,
            // 表示为权重不是最大可能权重(MAX_W),
            // 则对下一个顶点进行深度优先搜索
            if (_matrix[vertexIndex][i] != MAX_W)
                _DFS(i, visited);
        }
    }
}

// 公开的深度优先搜索函数,从给定的顶点开始
void DFS(const V& vertex)
{
    // 查找给定顶点在顶点列表中的索引
    size_t vertexIndex = FindSrci(vertex);
    
    // 初始化一个布尔向量来跟踪每个顶点是否已被访问
    vector<bool> visited(_vertex.size(), false);

    // 调用私有辅助函数进行深度优先搜索
    _DFS(vertexIndex, visited);
}

在这里插入图片描述
(注:本人是小潮院长粉丝,该文章中举的例子不代表真实好友关系,无意挑拨小潮院长内部成员关系,如有冒犯,请不要打我…)

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Python新手入门指南】pip安装失败、下载慢、pip换源

文章目录 前言一、换源的基本命令是什么&#xff1f;二、如何从官方来换源总结 前言 对于Python新手而言&#xff0c;使用pip安装包就会成为一个问题&#xff0c;因为国内下载慢&#xff0c;甚至可能下载不成功&#xff0c;课程要安装库&#xff0c;但是连库都安装不成功&…

20240705 每日AI必读资讯

&#x1f4da;Retool 刚刚发布了最新2024上半年《人工智能现状报告》 - 收集了约750名技术人员的意见 - 包括开发者、数据团队和各行业的领导者&#xff0c;了解如何利用人工智能产生真正的影响。 &#x1f517; 2024上半年《人工智能现状报告》Retool刚刚发布了最新-CSDN b…

瑞数信息:智能防护新时代,看AI如何筑起网络防线

AI时代&#xff0c;网络安全危与机并行。 尤其是近年来大火的大模型&#xff0c;对于网络安全行业的影响与其他行业有所不同&#xff0c;一方面&#xff0c;AI能够通过大幅降低了安全攻击的门槛&#xff0c;网络威胁的复杂性和多样性不断增加&#xff0c;如自动化攻击、零日漏…

记录问题:解决vscode找不到Python自定义模块,报错No module named ‘xxx‘

1. 背景 我非要用vscode&#xff0c;不用pycharm&#xff0c;哼&#xff01; 2. 问题 由于 import xx 自定义的模块&#xff0c; python run 的时候会报错 No module named ‘xxx‘ 报错信息&#xff1a; Traceback (most recent call last):File "d:\work\sf_financ…

原创作品 —(金融行业)年金系统交互和视觉设计

金融行业软件交互设计要点&#xff1a;“简化操作流程&#xff0c;确保流畅易用&#xff0c;同时注重交易环境的安全可靠&#xff0c;通过个性化体验提升用户满意度&#xff0c;并及时收集反馈以持续优化。” 2.UI设计要点&#xff1a;“注重视觉效果与用户体验的平衡&#xff…

创新与技术管理国际研讨会(ISITM 2024)

随着全球科技日新月异的进步&#xff0c;创新与技术管理在国际舞台上的地位愈发重要。在这样的背景下&#xff0c;创新与技术管理国际研讨会&#xff08;ISITM 2024&#xff09;应运而生&#xff0c;将于2024年12月6日至8日在中国长沙隆重举行。本次会议将聚焦创新与技术管理等…

【Linux开发实战指南】基于TCP、进程数据结构与SQL数据库:构建在线云词典系统(含注册、登录、查询、历史记录管理功能及源码分享)

目录 项目演示&#xff1a; 1. 主界面 技术讲解&#xff1a; TCP连接 进程的并发 链表 SQLite3 IO对文件的读写 功能实现 实现逻辑 我遇到的问题&#xff1a; 服务器端代码思路解析 必要条件 步骤详解 客户端代码思路解析 步骤详解 服务器源码如下&#xff1a;…

论文学习——基于区域多向信息融合的动态多目标优化引导预测策略

论文题目&#xff1a;Guided prediction strategy based on regional multi-directional information fusion for dynamic multi-objective optimization 基于区域多向信息融合的动态多目标优化引导预测策略&#xff08;Jinyu Feng a, Debao Chen b,c,d,∗, Feng Zou b,c, Fan…

【Git-驯化】一文学会git配置用户信息,git config用法细节

【Git-驯化】一文学会git配置用户信息&#xff0c;git config用法细节 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免费获取相关内容文档…

深度解码:需求跟踪的艺术与实战应用

文章目录 引言一、需求跟踪的定义二、需求跟踪矩阵2.1 需求跟踪矩阵包含的内容2.2 跟踪矩阵层级2.3 需求属性2.4 参考表格 三、需求跟踪的收益3.1 确保商业价值最大化3.2 满足客户期望3.3 范围管理3.4 决策支持3.5 提高效率和效果3.6 文档化和沟通3.7 变更管理3.8 测量和改进 四…

ll命令在ubuntu下不能使用的解决方案

ll命令在ubuntu下不能使用的解决方案 问题&#xff1a; ll命令在ubuntu下不能使用&#xff0c; 在Ubuntu终端里执行ll,提示:command not found 解决方案&#xff1a; 打开当前用户目录下的.bashrc文件 找到下面的内容&#xff0c;将前面的“#”去掉 #alias llls -alF 然…

S272钡铼技术4G无线RTU支持多路DIN输入和模拟量转换至4G网络

钡铼第四代RTU S272是一款先进的工业级4G远程遥测终端&#xff0c;为各种远程工业数据采集和控制系统提供了高效解决方案。结合了现代通信技术和多功能的输入输出接口&#xff0c;S272不仅支持多路数字量和模拟量输入&#xff0c;还具备灵活的扩展性和强大的控制功能&#xff0…

数据库表导出到excel:前置知识1 ALL_TAB_COLS

ALL_TAB_COLS 当前用户可访问的表、视图和群集的列的相关信息 其中几个字段: OWNER&#xff1a;表&#xff0c;视图及群集的Owner   TABLE_NAME&#xff1a; 表&#xff0c;视图及聚簇的名称   COLUMN_NAME&#xff1a; 字段名   DATA_TYPE &#xff1a;字段的数据类型…

君子签区块链+AI,驱动组织实现高效合同管理、精准风险控制

在传统合同签署的过程中&#xff0c;企业、组织、机构都面临着合同签署与管理的诸多问题和挑战&#xff1a;合同种类繁多、数量庞大导致起草效率低下&#xff1b;管理流程繁琐、权限分散使得审批周期冗长且效率低下&#xff1b;合同签订版本难以精准复核&#xff0c;风险防控更…

7.基于SpringBoot的SSMP整合案例-表现层开发

目录 1.基于Restfu1进行表现层接口开发 1.1创建功能类 1.2基于Restful制作表现层接口 2.接收参数 2使用Apifox测试表现层接口功能 保存接口&#xff1a; 分页接口&#xff1a; 3.表现层一致性处理 3.1先创建一个工具类&#xff0c;用作后端返回格式统一类&#xff1a;…

如何利用小程序容器技术搭建小程序生态?

小程序&#xff0c;作为现代移动互联网生态中的重要基础设施&#xff0c;正以其独特的创新性和便捷性展现出勃勃生机。截至2021年&#xff0c;全网小程序的数量已经突破了700万&#xff0c;其中微信小程序的开发者达到了300万之多。这一数字不仅代表了小程序在技术层面的成熟度…

Java项目总结3

1.抽象类与抽象方法 注意&#xff1a; 抽象类不能实例化 抽线类中不一定有抽i像方法&#xff0c;有抽象方法的类一定是抽象类 可以有构造方法 抽象类的子类要么重写抽象类中的所有抽象方法&#xff0c;要么是抽象类 抽象类的作用&#xff1a; 抽取共性时&#xff0c;无法确定方…

Linux:网络配置命令

目录 一、查看网络接口信息 ifconfig 二、修改网络配置文件 三、设置网络接口参数 ifconfig 四、查看主机名称 hostname 五、查看路由表条目route 5.1、查看路由 5.2、添加、删除静态路由条目 5.3、添加、删除默认网关记录 六、netstat命令 七、ss 命令 八、测试网络…

java web 部分

jsp作用域由大到小 过滤器有哪些作用&#xff1f; 过滤器的用法&#xff1f;&#xff08;对客户端的请求统一编码和对客户端进行认证&#xff09; JSP和Servlet中的请求转发分别如何实现&#xff1f; JSP 和 Servlet 有哪些相同点和不同点&#xff0c;他们之间的联系是什么…

恭喜!H医生一个月内荣获美国芝加哥大学访问学者邀请函

➡️【院校背景】 芝加哥大学&#xff08;英文&#xff1a;The University of Chicago&#xff0c;简称UChicago、“芝大”&#xff09;由石油大王约翰洛克菲勒于1890年创办&#xff0c;坐落于美国伊利诺伊州芝加哥市&#xff0c;一所私立研究型大学&#xff0c;属于全球大学校…