宽度优先搜索算法(BFS)详解(超级详细讲解,附有大图)

目录

一.宽度优先搜索(BFS)是什么?

二.图解宽搜(BFS)

三.对比与发现

四。工具——队列

 五.模板

六.最后


一.宽度优先搜索(BFS)是什么?

百度百科这样说:

宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。

过于理论性,不过说出了核心:

 它是一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。

 也有人这样说:

就是从根结点位置开始遍历,遍历结束后依次遍历与根节点相邻的点,相邻点遍历结束后继续遍历上轮第一个遍历结点的相邻的未遍历的点,直至所有的点都遍历结束。

generalKnowledge/基础算法/广度优先搜索.md · jimmyflower6/中山一中初中部信息竞赛 - Gitee.com

 这个说的比较好,但仍然有一点生硬。

简单来说,就是:

宽度优先搜索是一种对图进行搜索的算法。假设我们一开始位于某个顶点(即起点),此 时并不知道图的整体结构,而我们的目的是从起点开始顺着边搜索,直到到达指定顶点(即终 点)。在此过程中每走到一个顶点,就会判断一次它是否为终点。广度优先搜索会优先从离起点 近的顶点开始搜索。

下面,我将用步骤图的方式,向大家介绍这种方法。


二.图解宽搜(BFS)

 A为起点,G为终点。一开始我们在起点A上,此时并不知道G在哪里。

将可以从A直达的三个顶点B、C、D设为下一步的候补顶点。 

从候补顶点中选出一个顶点。优先选择最早成为候补的那个顶点,如果多个顶点同时成为候补,那么可以随意选择其中一个。 (优先选择最早成为候补的那个顶点,这是一种“先进先出”的方式,后面会讲到)

此处 B、C、D 同时成为候补,所以我们随机选择了最左边的顶点B。 

移动到选中的顶点B上。此时我们在B上, 所以B变为红色,同时将已经搜索过的顶点变为橙色。

此处,候补顶点是用“先入先出”(FIFO)的方式来管理的,因此可以使用“队列”这个数据结构。(这个结构将会在后面讲解,请往下翻)

将可以从B直达的两个顶点E和F设为候补 顶点。

此时,最早成为候补顶点的是C和D,我们选择了左边的顶点C。 

移动到选中的顶点C上。

将可以从C直达的顶点H设为候补顶点。 

重复上述操作直到到达终点,或者所有的顶点都被遍历为止。 

这个示例中的搜索顺序为 A、B、C、D、E、F、 H、I、J、K。 

完成了从A到I的搜索,现在在顶点J处。 

到达终点G,搜索结束。

补充说明:由上可以知道,广度优先搜索的特征为从起点开始,由近及远进行广泛的搜索。因此,目标顶点离起点越近,搜索结束得就越快。


三.对比与发现

DFS和BFS的区别
bfs 遍历节点是先进先出,dfs遍历节点是先进后出
bfs是按层次访问的,dfs 是按照一个路径一直访问到底,当前节点没有未访问的邻居节点时,然后回溯到上一个节点,不断的尝试,直到访问到目标节点或所有节点都已访问。
bfs 适用于求源点与目标节点距离最近的情况,例如:求最短路径。dfs 更适合于求解一个任意符合方案中的一个或者遍历所有情况,例如:全排列、拓扑排序、求到达某一点的任意一条路径。

不过,宽度优先搜索(BFS)的先进先出如何实现呢?这里,我们运用了一个数据结构——队列。 


四。工具——队列

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

我们可以用双指针标记一下,通过front指针与rear指针,对队头和队尾进行标记,然后只允许在front、rear指针的位置进行增删改查,那么这样便实现了对数组的受限。这是一种运用数组的数据结构对队列的模拟。初学者建议先用这种方式熟悉队列。

具体操作:

/*
    通常将front赋值为0,rear赋值为-1
    方便后续进队、出队以及取队首元素
 */
int a[100], front=0, rear=-1;

// 进队
a[++rear] = 10;

// 出队
front++

// 取队首元素
a[front]

// 取队尾元素
a[rear]

// 判断是否为空队
if(front > rear)
    cout << "该队列为空队";

不过,到了后期,为了节省时间,我们可以直接用c++自带的STL容器来完成操作。

具体操作如下:

// 导入queue包
#include<queue>

// 申明一个queue对象
// 填入你想装填的数据类型
queue<int> qu;

// 进队
int a = 10;
qu.push(a);

// 出队,无返回值
qu.pop();

// 取队首元素
int front = qu.front();

// 取队尾元素
int rear = qu.back();

// 队是否为空
if(qu.empty()) {
    cout << "该队列为空队";
}

// 队大小,返回int
qu.size();

 五.模板

BFS的题目是有一套明显模板的,不信,我们来看看。(将展示核心代码)

八数码问题:


    while(!q.empty())//q非空,可以走
    {
        for(int i=0;i<4;i++)//四个方向
        {
            ma ac=q.front();
            int nx = ac.x0 + dx[i];
            int ny = ac.y0+ dy[i];
            if(nx>=1&&ny>=1&&nx<=3&&ny<=3)
            {
                swap(ac.a[ac.x0][ac.y0],ac.a[nx][ny]);
                ac.x0=nx;
                ac.y0=ny;
                //将0与目标数交换
                ac.ans++;//步数加1
                ac.kt=kt(ac);
                //康托判重
                 if (!flag[ac.kt])
                {
                    flag[ac.kt] = 1;
                    q.push(ac);
                    //加入队列
                    if(ac.kt==mo.kt)
                    { 
                        printf("%d",q.back().ans);
                        exit(0);
                    }
                }
            }
        }
        q.pop();
        //弹出已遍历完所有情况的矩阵
    }

海上救援任务:

		while(!q.empty())
		{
			for(int i=0;i<4;i++)
			{
				node ne;
				ne.x=q.front().x+dx[i],ne.y=q.front().y+dy[i];
				if(bo[ne.x][ne.y]==0&&ne.x>=0&&ne.y>=0&&ne.x<=n&&ne.y<=m&&a[ne.x][ne.y]==1)
				{
					ne.ans=q.front().ans+1;
					bo[ne.x][ne.y]=1;
					q.push(ne);
					if(ne.x==xe.x&&ne.y==xe.y)
					{
						flag=true;
						printf("%d\n",q.back().ans);
						break;
					}
				}
			}
			if(flag) break;
			q.pop(); 
		}

 巧妙取量:

    while(scanf("%d%d%d%d",&t.a[1],&t.a[2],&t.a[3],&k)!=EOF)//多组数据
    {
        flag=false;
        memset(bo,0,sizeof(bo));
         
    	bo[t.a[1]][t.a[2]][t.a[3]]=1;
    	p.ans=0;
    	p.a[1]=t.a[1];
    	p.a[2]=p.a[3]=0;
    	q.push(p);
     	if(q.front().a[1]==k||q.front().a[2]==k||q.front().a[3]==k)
    	{
    	    printf("yes\n%d\n",0);
            continue;
    	}//特判 
    	while(!q.empty())
    	{
        	for(int i=1;i<=3;i++)
        	{
          		if(q.front().a[i]>0)
            	{
                	for(int j=1;j<=3;j++)
                	{
                    	node sy;
                    	sy=q.front();
                    	sy.ans ++;
                    	if(i!=j)
                    	{
                        	int T=t.a[j]-sy.a[j];
                        	if(sy.a[i]>T)
                        	{
                            	sy.a[j]=t.a[j];
                            	sy.a[i]=sy.a[i]-T;
                        	}
                        	else
                        	{
                            	sy.a[j]+=sy.a[i];
                            	sy.a[i]=0;
                        	}
                        	if(!bo[sy.a[1]][sy.a[2]][sy.a[3]])
                        	{
                            	bo[sy.a[1]][sy.a[2]][sy.a[3]]=1;
                            	if(sy.a[1]==k||sy.a[2]==k||sy.a[3]==k)
                            	{
                                	flag=true;
                            	}
                            	q.push(sy);
                        	}
                    	}
                	}
            	}
          
        	}//遍历每一种倒法 
        if(flag) break;
        q.pop();
    	}
    	if(flag)
    	{
        	printf("yes\n%d\n",q.back().ans);
    	}
    	else
    	{
        	printf("no\n");
    	}
    	while(!q.empty())
    	{
        	q.pop();
    	}
    }

我选了几道没那么相似的题目,但仔细一看,架构都是一样的。

用步骤图可表示为:

 而用伪代码,则是这样表示:

int BFS(Node start, Node target) {
    入队(初始状态);
    visited[初始状态] = true;
    while(!空队) {
        for() { // 状态转换
            Node node = 队首元素;

            对node的处理,生成新node状态;

            if (visited[node] == true)
                continue;
            if (node == target) {
                输出答案;
                return 0;
            }
            v[node] = true;
            入队(node);
        }
    出队();
    }
    
}

这,就是BFS问题的模板!!!

六.最后

没什么,如果喜欢的话,给个三连吧!您的支持是我最大的动力!

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

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

相关文章

蓝桥杯省赛无忧 STL 课件15 queue

01 queue队列 02 priority_queue优先队列 接下来介绍几种优先队列修改比较函数的方法 03 deque双端队列 04 例题讲解 https://www.lanqiao.cn/problems/1113/learning/?page1&first_category_id1&problem_id1113输入 5 IN xiaoming N IN Adel V IN laozhao N OUT …

抓交通肇事犯(python)

问题描述&#xff1a; 一辆卡车违反交通规则&#xff0c;撞人后逃跑。现场有三人目击该事件&#xff0c;但都没有记住车号&#xff0c;只记下了车号的一些特征。甲说&#xff1a;牌照的前两位数字是相同的&#xff1b;乙说&#xff1a;牌照的后两位数字是相同的&#xff0c;但…

【金融数据分析】计算2023年沪深300行业涨跌幅

本文我们来计算2023年沪深300行业涨跌幅&#xff0c;最后呈现的页面是这样的 先来说后端的代码&#xff0c;计算行业涨跌幅的原理就是将某个行业所有成分股的涨跌幅加起来&#xff0c;然后除以股票数量得到一个平均涨跌幅 // 查询行业涨跌幅public List<CSI300IndustryRankV…

如何将.NET 8.0的ASP.NET Core Web API部署成Windows服务

写在前面 前面写了一篇关于将.NET应用转换成Windows服务的方法&#xff0c;其实真正的目的是为了探索如何将Asp.Net Core Web Api 部署成Windows 服务。基于上一篇的基础&#xff0c;只需把创建 WebApplication 的代码放到 BackgroundService 的ExecuteAsync方法中即可。 其中…

【一】通信协议概述

通信协议概述 简介&#xff1a; 很早之前就思考了要写一下电力系统常用的几种通信协议&#xff0c;一直拖着也没有行动&#xff0c;这次终于下定决心来出一个《通信协议》这样的专栏。电力行业数字化方面资料较少&#xff0c;我理解主要一方面是数字化程度还不高&#xff0c;一…

笔记系统的部署架构

前天给笔记系统打了0.0.3的tag&#xff0c;一个简单的全栈功能闭环基本完成。既然是开源&#xff0c;因此&#xff0c;这里有必要分享一下部署结构&#xff0c;希望能够获得小伙伴们的反馈。 目前整个系统采用docker容器来部署。应用介绍 auth_app: 登录/注册的前端应用 web_ap…

WebRTC入门:基础的核心协议与概念(二十三)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 优质专栏:多媒体系统工程师系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只…

Open3D对产生偏差的点云数据进行校正

由于相机安装问题&#xff0c;导致点云数据两边翘起来&#xff0c;为了计算把翘起来的部分拉平整 import time import open3d as o3d; import numpy as np; import matplotlib.pyplot as plt from scipy.signal import find_peaks import pandas as pdOriginalPly o3d.io.rea…

自养号测评:掌握Shopee运营黑科技的必备攻略

虾皮卖家们经常挂在嘴边的“权重”&#xff0c;简而言之&#xff0c;就是商品或店铺在Shopee平台上受到重视的程度。它的存在是为了评估商品或店铺是否满足用户需求&#xff0c;能否助力订单转化&#xff0c;为平台创造更多收益。就像我们给孩子们打分一样&#xff0c;分数越高…

Vue3函数式弹窗实现

要在一些敏感操作进行前要求输入账号和密码&#xff0c;然后将输入的账号和密码加到接口请求的header里面。如果每个页面都去手动导入弹窗组件&#xff0c;在点击按钮后弹出弹窗。再拿到弹窗返回的账号密码后去请求接口也太累了&#xff0c;那么有没有更简单的实现方式呢&#…

python_locust压测

可以在命令行启动&#xff0c;cmd命令框&#xff0c;进入此文件的目录输入&#xff1a; locust -f .\dash.py --host"https://litemall.hogwarts.ceshiren.com" -f&#xff1a; 指定性能测试脚本文件的绝对路径。 –host&#xff1a; 指定被测试应用的URL的地址&…

Qt/QML编程学习之心得:Grid、GridLayout、GridView、Repeater(33)

GRID网格用处非常大,不仅在excel中,在GUI中,也是非常重要的一种控件。 Grid 网格是一种以网格形式定位其子项的类型。网格创建一个足够大的单元格网格,以容纳其所有子项,并将这些项从左到右、从上到下放置在单元格中。每个项目都位于其单元格的左上角,位置为(0,0)。…

【JavaScript】深度理解js的函数(function、Function)

简言 学了这么久的JavaScript&#xff0c;函数在JavaScript中最常用之一&#xff0c;如果你不会函数&#xff0c;你就不会JavaScript。 函数就是Function对象&#xff0c;一个函数是可以通过外部代码调用的一个“子程序”&#xff0c;它是头等&#xff08;first-class&#xf…

微机原理常考简答题(二)

一&#xff0c;简述8086CPU响应可屏蔽中断的条件及过程。 CPU响应可屏蔽中断的条件是有中断请求&#xff0c;中断标志IF1开中断&#xff0c;现行指令执行结束。 CPU响应可屏蔽中断的过程&#xff1a;CPU在INTR引脚上接到一个中断请求信号&#xff0c;如果此时IF1&#xff0c;并…

group by 查询慢的话,如何优化?

1、说明 根据一定的规则&#xff0c;进行分组。 group by可能会慢在哪里&#xff1f;因为它既用到临时表&#xff0c;又默认用到排序。有时候还可能用到磁盘临时表。 如果执行过程中&#xff0c;会发现内存临时表大小到达了上限&#xff08;控制这个上限的参数就是tmp_table…

基于完整熵编码系数组的JPEG图像加密方案

论文题目&#xff1a;JPEG image encryption with grouping coefficients based on entropy coding 期刊&#xff1a;Journal of Visual Communication and Image Representation 分区&#xff1a;中科苑三区&#xff0c;老牌图像处理期刊 文章目录 摘要概要整体架构流程实验结…

mac怎么拼图?Mac拼图技巧分享

mac怎么拼图&#xff1f;在Mac上拼图是一种令人愉悦的创意表达方式&#xff0c;可以让你将多张图片巧妙地融合在一起&#xff0c;创造出令人惊叹的艺术品。本文将向你介绍在Mac上进行拼图的几种方法&#xff0c;帮助你轻松实现这一目标。 一、使用Mac内置的预览功能进行拼图 M…

100个GEO基因表达芯片或转录组数据处理之GSE159676(002)

写在前边 虽然现在是高通量测序的时代&#xff0c;但是GEO、ArrayExpress等数据库储存并公开大量的基因表达芯片数据&#xff0c;还是会有大量的需求去处理芯片数据&#xff0c;并且建模或验证自己所研究基因的表达情况&#xff0c;芯片数据的处理也可能是大部分刚学生信的道友…

物联网协议Coap之Core和NetWork简介

目录 前言 一、Coap的Core包 1、Coap对象 2、Message对象 3、Request对象 4、Response对象 二、Coap的NetWork调试 1、UDP运行模式 2、Network消息接收 3、Sender线程发送数据 三、总结 前言 在之前的博文中&#xff0c;对Californium中Coap的实现进行了简要的介绍&a…

IT从业人员如何养生?

目前&#xff0c;电脑对人体生理和心理方面的负面影响已日益受到人们的重视。为此科学使用电脑&#xff0c;减少电脑和网络的危害是十分必要的。好代码网总结了一些it从业人员的保健知识&#xff0c;分享给大家。 一是要增强自我保健意识 工作间隙注意适当休息&#xff0c;一般…