首页 > 其他分享 >广度(宽度)优先搜索(遍历)bfs详解

广度(宽度)优先搜索(遍历)bfs详解

时间:2024-07-11 16:27:37浏览次数:17  
标签:遍历 int bfs vis 详解 mp push 起点

简介

        广度优先搜索(遍历)是一种在图的搜索遍历中较常见的算法。它的时间复杂度通常要比深度优先搜索(遍历)要低很多,尤其是最短路。这是因为深度优先的思想是走一条路要把它走到底再去考虑别的路,如果一开始走错了,后面会浪费很多时间在死胡同上,而且递归的方法本来就需要来一次回一次。而广度优先的思想则是让每一条路都向前进发一格,那么走错路不用付出太多代价,而且这样你第一次遇到终点就是答案,因为你每条路都是同层次。层次越少,答案越好。dfs那就惨了,要弄出所有的答案进行对比。它还无需考虑递归出所以函数的return的问题。它还不会那么容易爆栈。

思想实现

        bfs基本都是用队列实现的。因为它是先进先出的。先进去的结点永远都是先搜完的。

        那队列是怎么操作的呢?别急,慢慢来。首先,我们要把起点push进队列,表示它即将搜索,并标记表示搜过的数组vis记下vis[起点]=1。然后,将起点的数据(坐标、步数、状态等)都保存在一个临时变量里,并将起点pop掉,因为它接下来搜完使命就结束了。接着,我们将所有起点(根据临时变量中的数据)能到达合适没有搜过(!vis[该点])的点都放进队列中,表示一会儿就要搜,再让vis[该点]=1,数据根据上个点按需求更改。之后每个点都像起点一样操作,不过是没有“首先”这一步骤的,因为在上个点搜索是它就设置好了。最后,如果队空,说明没有更多结点了,所以就退出。

        对于下图,我给出了它的bfs和dfs搜索顺序:

代码实现

1.模版

(代码为c++语言)

定义图

string mp[15];
/*其他数据类型也可以,一维二维三维按需求。*/

定义队列(需要加头文件<queue>)

queue<node/*某数据类型*/>Q;

vis数组

bool vis[/*大小按需填写*/]; 

定义函数

void bfs(/*起点数据*/){}

初始化起点

Q.push(/*起点数据*/);
vis[/*起点*/]=1;

中间部分

while(!Q.empty()){
	node/*队列Q的数据类型*/ temp=Q.front();
	Q.pop();
	for(/*枚举每个Q.front()能到达的点*/){
		if(vis[/*该点*/]||/*别的不合适的条件*/) continue;
		//操作
		Q.push(/*该点*/);
		vis[/*该点*/]=1; 
	}
}

2.实战练习

1.走迷宫

题目要求

        n*m的迷宫。S是起点,E是终点,#是墙壁,*是路。一段路需要1分钟行走。输出S到E的最短时间是几分钟。如果不能到达,输出-1。请使用bfs。0<n,m<=10^4

输入

        输入n和m,之后n行每行m个字符。

输出

        输出要求的数。

代码:

#include <iostream>
#include <queue>
using namespace std;
const int N=1e4+5;
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};  //方向数组,为了更方便移动,迷宫中有四个方向x±1和y±1 
bool vis[N][N];
string mp[N];
struct node{
	int x;
	int y;
	int step;
};
queue<node>Q;
int bfs(int stx,int sty,int n,int m){
	Q.push({stx,sty,0});
	vis[stx][sty]=1;
	while(!Q.empty()){
		node temp=Q.front();
		Q.pop();
		for(int i=0;i<4;i++){
			int nx=temp.x+dx[i],ny=temp.y+dy[i],ns=temp.step+1;
			if(vis[nx][ny]||nx<1||ny<1||nx>n||ny>m||mp[nx][ny]=='#') continue;
			if(mp[nx][ny]=='E') return ns;
			Q.push({nx,ny,ns});
			vis[nx][ny]=1; 
		}
	} 
	return -1;
}
int main(){
	bool t=false;
	int n,m,stx,sty; cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>mp[i];
		mp[i]=' '+mp[i];  //坐标从1开始防溢出
		if(!t){
			for(int j=1;j<=m;j++){ //找起点终点 
				if(mp[i][j]=='S'){
					stx=i,sty=j;  //x在这是行y是列 
					t=true;
				}
			}	
		}
	}
	cout<<bfs(stx,sty,n,m);
	return 0;
}

不过你也能看到,编程复杂度有点高。。。

2.图的最短路

题目要求

        给出n个点m条边的无向图,告诉我从点1到其他所有点的最短路(换行隔开)。

        保证每个点都是连通的。0<n,m<10^4

输入

        首先输入n和m。然后m行输入两个数u,v,表示u和v相连。

输出

        按照题意输出。

提示:

        回想vector查看连接点的方法或查看我的这篇文章。文章icon-default.png?t=N7T8https://blog.csdn.net/weixin_72829799/article/details/140257376?spm=1001.2014.3001.5501

代码:

#include <iostream>
#include <queue>
using namespace std;
const int N=1e4+5;
int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};  //方向数组,为了更方便移动,迷宫中有四个方向x±1和y±1 
bool vis[N];
int dis[N];
vector<int>mp[N];
queue<int>Q;
void bfs(){
	Q.push(1);
	dis[1]=0;
	vis[1]=1;
	while(!Q.empty()){
		int temp=Q.front();
		Q.pop();
		for(auto v:mp[temp]){
			if(vis[v]) continue;
			dis[v]=dis[temp]+1;
			Q.push(v);
			vis[v]=1; 
		}
	} 
}
int main(){
	int n,m; cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v; cin>>u>>v;
		mp[u].push_back(v);
		mp[v].push_back(u);
	}
	bfs();
	for(int i=2;i<=n;i++){
		cout<<dis[i]<<'\n';
	}
	return 0;
}

运行结果: 

OK,恭喜你完成所有题目!

小结

        bfs是一个运用场景广泛的算法,时间复杂度也还不错,非常实用。欢迎大家在评论区指点反馈我在文章撰写中的不足!

标签:遍历,int,bfs,vis,详解,mp,push,起点
From: https://blog.csdn.net/weixin_72829799/article/details/140313621

相关文章

  • C# Winform之propertyGrid控件使用详解和分组设置
    PropertyGrid控件在WinForms中是一个非常有用的工具,它允许用户查看和编辑一个对象的属性。这个控件非常适合用于配置对话框或任何需要动态显示对象属性的地方。下面我会详细介绍PropertyGrid的使用方法和如何对属性进行分组。使用详解1.添加 PropertyGrid 控件在Vi......
  • 加密算法详解:对称加密、非对称加密、Hash算法
    对称加密、非对称加密和哈希算法是信息安全中的三种主要加密技术,它们各自有不同的特点和用途:对称加密(SymmetricEncryption)工作原理:使用相同的密钥进行加密和解密。速度:通常非常快,适合大量数据的加密。密钥管理:参与通信双方必须安全地共享密钥,密钥泄露会导致安全风险。主......
  • 归并排序详解
    文章目录归并排序原理排序演示1排序演示2代码实现递归方式迭代方式复杂度分析时间复杂度空间复杂度稳定性归并排序原理归并排序,也是应用了分治的思想。将原数组分为两个子数组,左子数组和右子数组,两个子数组排好序后,通过拷贝的方法将两个数组边排序边合并。子数组可......
  • vue中的插槽详解
    插槽(slot)插槽在vue中是一种很常见的写法,让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式一共有三种分类:默认插槽、具名插槽、作用域插槽,下面一一根据案例改造说明1基本案例首先编写一个基本的案例,三个组件展示不同的数据类型 页面进行展示 现在要......
  • BFS:边权相同的最短路问题
    一、边权相同最短路问题简介二、迷宫中离入口最近的出口.-力扣(LeetCode)classSolution{public:constintdx[4]={1,-1,0,0};constintdy[4]={0,0,1,-1};intnearestExit(vector<vector<char>>&maze,vector<int>&e){intm=maze.size(),n=......
  • 详解C#委托与事件
    在C#中,委托是一种引用类型的数据类型,允许我们封装方法的引用。通过使用委托,我们可以将方法作为参数传递给其他方法,或者将多个方法组合在一起,从而实现更灵活的编程模式。委托类似于函数指针,但提供了类型安全和垃圾回收等现代语言特性。基本概念定义委托定义委托需要指定它所代......
  • 一文详解大语言模型的流行架构与训练技术
    这篇博客全面介绍了大型语言模型(LLMs)的构建流程,从流行架构的选择到实际建模的每个关键步骤。文章首先探讨了LLMs的模型架构,然后详细阐述了数据准备过程,包括数据的收集、清洗和去重,接着是关于如何进行有效标记化的讨论。在模型构建方面,博客详细解释了采用自监督学习方法的预......
  • Memcached介绍和详解
    Memcached介绍和详解Memcached是一个高性能的分布式内存对象缓存系统,通过在内存中缓存数据来减少数据库的读取次数,从而提高动态Web应用程序的速度和效率。下面将详细介绍Memcached的安装、配置和使用方法。Memcached简介Memcached是一个基于内存的缓存系统,它通常用于缓......
  • 阿里云镜像仓库的使用详解
    一、镜像仓库介绍Registry是Docker公司的一项创新,它提供了存放镜像的仓库服务。在构建好镜像后,我们通常会将镜像上传到Registry服务器上进行保存。这样可以保证不会因本机故障而导致镜像丢失,同时,其他机器也能很方便地通过网络方式下载。DockerHub即为Docker官方的Registry服务......
  • ES6 Reflect 详解(三)
    Reflect对象与Proxy对象一样,也是ES6为了操作对象而提供的新API。Reflect对象的设计目的有4个。将Object对象的一些明显属于语言内部的方法(比如Object.defineProperty),放到Reflect对象上。现阶段,某些方法同时在Object和Reflect对象上部署,未来的新方法将只......