浅谈(保姆级讲解)宽度优先搜索(bfs)

本文介绍了宽度优先搜索(BFS)算法,如何在迷宫问题中寻找从起点到终点的最短路径,并通过代码示例展示了如何使用队列实现BFS算法。算法的核心思想是优先探索离起点近的节点,确保找到全局最短路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.什么是宽度优先搜索

从字面意思上说(比较正规,看看就行):

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

从图上来看(这个更易于理解):

现在有一棵树(地图),在这个上面我们现在在A点,但如果我们想去G点,那么bfs算法中

要求我们寻找的时候宽度优先即横着找。因此如果要找从A点到G点的最短路径,我们会先找A,当这一行找完之后,再找B点、C点、D点,第二行找完之后,我们接着第三行展开找E点知道找到G点,这是就大功告成了。

但为什么这样找?我们先看一个例子。

走迷宫
给定一个n*m的二维整数数组,用来表示一个迷宫,数组中只包含0或1,其中0表示可以走的路,1表示不可通过的墙壁。
最初,有一个人位于左上角(1, 1)处,已知该人每次可以向上、下、左、右任意一个方向移动一个位置。
请问,该人从左上角移动至右下角(n, m)处,至少需要移动多少次。
数据保证(1, 1)处和(n, m)处的数字为0,且一定至少存在一条通路。

输入格式
第一行包含两个整数n和m。
接下来n行,每行包含m个整数(0或1),表示完整的二维数组迷宫。

输出格式
输出一个整数,表示从左上角移动至右下角的最少移动次数。

数据范围

1≤n,m≤100
1
输入样例:

5 5
0 1 0 0 0
0 1 0 1 0
0 0 0 0 0
0 1 1 1 0
0 0 0 1 0
1
2
3
4
5
6
输出样例:

8

我们可以在每次出发的时候,走到离自己最近的点(贪心思想),由此我们每次都保证走最近的,那从局部最近推整体最近,直到走到终点,必有一条路是整体最近的。所以我们可以利用BFS做最短路问题。

在这个迷宫当中,我们要走到(n,m)点的话是不能用dfs广度搜索一条道走到黑的,我们需要稳扎稳打,走一个点记录一个点,只要走到(n,m)点都记录完为止。这就是现在说的bfs算法。

二.代码(板子)

1.待我先解释一下为什么要用队列(用来解决移动到哪个点):

对于上面那个图,我们从A点开始时,让A点入队,对A点附近的点进行查找

之后A点附近搜索完之后,A点出队,让B点、C点、D点入队:

直到最后一直进行这个步骤:

2.上面解决了移动到哪个点的问题,我们接着解决怎么移动的问题:

int dx[4]={0,0,-1,1};//表示要走的四个方向

int dy[4]={1,-1,0,0};

我们使用这两行代码,对于一个点(x,y)坐标表示,如果想向左移动,就是x+(-1),y+0,那么向右就是x+1,y。

这是当我们到达点B后,我们让点B的x、y加上4个不同的移动方向,这时向上走会遇到A点向右走会遇到D点,这样就会把周围点搜索一遍。

注:同时要记得标记一下用过的点,确保不会重复使用。

for(int i=0;i<4;i++){
            int nex=top.first+dx[i];
            int ney=top.second+dy[i];
            //如果某一个方向的下一个位置没有走过,并且位置为零(即此题中可以通过)
            //记录此点,同时让该点入队,下一次以此点展开搜索
            if(nex>=0&&nex<n&&ney>=0&&ney<m&&mark[nex][ney]==-1&&mp[nex][ney]==0){
                mark[nex][ney]=mark[top.first][top.second]+1;
                q.push({nex,ney});
            }
        }

3.这里我们以上面这道经典题目为例进行代码展示:

#include<bits/stdc++.h>
using namespace std;
int n,m;
int dx[4]={0,0,-1,1};//表示要走的四个方向
int dy[4]={1,-1,0,0};
const int N=110;
int mp[N][N];//用来存储地图
int mark[N][N];//表示是否走过当前位置(未走过为-1),以及走了多远
int ans;
typedef pair<int,int> PII;
void bfs(){
    memset(mark,-1,sizeof mark);//都初始化为-1表示没有来过
    queue<PII> q;
    q.push({0,0});//让起点入队
    mark[0][0]=0;
    while(!q.empty()){
        PII top=q.front();//以队列每个元素为起点展开四个方向的搜索
        for(int i=0;i<4;i++){
            int nex=top.first+dx[i];
            int ney=top.second+dy[i];
            //如果某一个方向的下一个位置没有走过,并且位置为零(即此题中可以通过)
            //记录此点,同时让该点入队,下一次以此点展开搜索
            if(nex>=0&&nex<n&&ney>=0&&ney<m&&mark[nex][ney]==-1&&mp[nex][ney]==0){
                mark[nex][ney]=mark[top.first][top.second]+1;
                q.push({nex,ney});
            }
        }
        q.pop();//当这个节点搜索结束则出队
    }
    cout<<mark[n-1][m-1]<<endl;//输出最短路结果
}
int main(){
    cin>>n>>m;
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            cin>>mp[i][j];
        }
    }
    bfs();
    system("pause");
    return 0;
}


至此,功德圆满

入坑不易,记得轻轻点一下关注哦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值