广度优先搜索(BFS)(算法笔记)

本文内容基于《算法笔记》和官方配套练题网站“晴问算法”,是我作为小白的学习记录,如有错误还请体谅,可以留下您的宝贵意见,不胜感激。


前言

深度优先搜索是一种枚举所有完整路径以遍历所有情况的搜索方法,总是以“广度”作为前进的关键词,采用队列实现。

一、广度优先算法概述

广度优先搜索属于搜索问题的一种,当问题可以被描述为“路径搜索”时,就可以采用搜素问题的所有解的方式来进行解决,所以BFS本质还是暴力。广搜也存在“岔道口”,只是当遇到“岔道口”时,需要将本层所有的岔道都走一遍,然后再走下一层的岔道,符合队列的“先进先出”思想。
广搜和深搜的共同点之一是只有一个起点,也就是说一次搜索只能遍历以一个点为起点的所有路径。
而BFS更适合需要搜索最优解的问题,BFS可以理解为层次遍历,也就是说如果BFS搜索到了一个满足问题的答案,那么这个答案肯定是“距离最近的”,是“最内层的”,那么就不需要继续向外层搜索了,所以BFS对比DFS更适合找最优解。但BFS需要的空间比DFS大,需要将一层的所有可能解都放入队列中,不像DFS那样是走完一条完整路径再去走另一条路(不过递归实现本身占用的内存还是挺大的,除非自己创建栈)。

二、算法设计

BFS设计的关键在于“岔路口”的抽象和“层次”在队列中的具体体现。
算法通用模板:

void BFS(int s) {
   
   
	queue <int> q;
	q.push(s);
	while(!q.empty()){
   
   
		取出队首元素top;
		访问队首元素top;
		将队首元素出队;
		将top的下一层节点中未曾入队的节点全部入队,并设置为已入队; 
	} 
}

1.数字操作

在这里插入图片描述
岔路口:加1或乘2;
目标:到达指定整数;
采用队列将起点1加入队首元素,将1取出,并访问1(判断是否满足条件),对1 + 1和1 * 2进行判断,如果满足入队条件(<=n)就入队,循环执行该过程直到队列为空(无解,但根据题意肯定有解)或者找到最优解。
记录本层长度,设置计数器,如果访问完本层所有元素则将计数器自加。可以设置一个bool数组记录被访问过的元素,如果元素被访问过就不需要重复访问,大大节省算法时间。
在实际的编码中,我并没有严格按照BFS的思路去写,而是在被访问元素的下一层节点入队时直接进行访问,这样可以节省一层的搜索量,不过我也不知道这样做到底好不好。
完整代码如下:

#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;

const int MAXN = 100000;
bool inQueue[MAXN + 1] = {
   
   false};

int BFS(int n){
   
    //1是起点,+1或*2是岔路口 
	queue <int> qe;
	qe.push(1);   //放入起点
	int step = 0;
	while(!qe.empty()){
   
   
		int size = qe.size();   //记录本层长度 
		for(int i = 0; i <= size - 1; i++){
   
    
			int top = qe.front();  //取出队首元素 
			qe.pop();   
			inQueue[top] = true;
			if(top + 1 == n) return step + 1; //说明下一层
			if(top + 1 <= n && !inQueue[top + 1]) qe.push(top + 1);
			if(top * 2 == n) return step + 1;
			if(top * 2 <= n && !inQueue[top * 2]) qe.push(top * 2);
		} 
		step++;   //层数加一 
	}
	return -1;   //到不了目标点 
}

int main(){
   
   
	int n;
	scanf("%d", &n);
	printf("%d", BFS(n));
}

2迷宫最短路径

在这里插入图片描述
岔路口:(x + 1 , y)(x - 1 , y) (x , y + 1) (x , y - 1)
目标:到达终点的最少步数对应的路径
套用BFS的标准模板进行搜索,这道题到没有说不可以返回原来的位置,但可以证明如果存在最短路径,那这条路径一定不存在返回原来的位置的情况,所以可以设置bool散列表判断坐标是否入过队,这样可以防止元素同一元素反复入队,大大节省搜索层数。
关键是如何保存走过的路径,这在DFS里很容易实现,但BFS是层次搜索,不像DFS那样走完一条完整路径才返回去走其他路径,所以并不能采用具体的存储结构存储走过的路径(那样将耗费巨大的存储空间),这里提供两种方法:(1)模拟链表,记录前驱节点位置;(2)DFS + BFS
第二个方法效率较低,这里使用第一种方法,具体操作是开一个类似二叉树的静态二叉链表,只是链表节点中的地址是前驱节点的下标,故只能反向遍历。
具体代码如下:这段代码在网站中运行时有一个数据点运行无结果,但我在DEV中可以运行出正确答案,搞不懂······

#include<cstdio> //关键在于如何记录走过的路径 
#include<queue>
#include<algorithm> 
using namespace std;

const int MAXN = 100;
int number[MAXN][MAXN] = {
   
   }; //迷宫
int n , m;  //列数和行数 
int add1[] = {
  
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瓦耶_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值