BFS搜索与队列思想解决迷宫最短路径问题

在搜索算法中,最为简单的并且最为重要的要数BFS(宽度优先搜素),DFS(深度优先搜索)两种算法。搜索领域中的高深算法,都是以这两种算法为基础!灵活运用这两种算法,毫不夸张来说,可以解决编程中的任何问题(当然,如果忽略时间限制的话)。今天主要谈谈BFS在走迷宫问题的应用。

著名的沃斯科学家提过这样的公式 程序=算法+数据结构(顺便一提,对于软件工程领域有这样的公式 软件=程序+文档)。对应于BFS算法的数据结构,一般是一个队列(顺便提一下DFS对应的是栈)。

现在有这样的一个迷宫,S表示起始位置,E表示结束位置,在迷宫中有一些障碍‘*’,不能走障碍位置。那么问最少需要多少步可以从起始位置走到重点位置(当然,愿意的话,完全可以输出所走的路径,这些都是小问题)

首先,什么是BFS?百度百科的定义是:

BFS,其英文全称是Breadth First Search。 BFS并不使用经验法则算法。从算法的观点,所有因为展开节点而得到的子节点都会被加进一个先进先出的队列中......

我提一些个人的看法(为便于理解,有些地方并不准确),先上一张图。

自然界的宽搜

闪电是如何扩散的?我们发现闪电先是一道主光柱,然后由主光柱再扩展出许多分支,那么是否能联想到离散里的树?以下是一棵树:

BFS搜索的话就是先对行1的节点1进行搜索,看它是否符合条件,否则继续搜索行1的节点1的所有子节点,即行2的所有节点,看是否有节点符合条件,如果没有则依次以行2的节点为根继续往下搜索行3的子节点,如此重复直到搜索到目标节点或者将所有节点搜索完为止。以上是BFS最简单的搜索思想。接下来我们将这一思想具体到一个迷宫中。

公主晕倒在了火海之中,王子准备前往营救(╯▽╰),但是王子不能越过途中的火海,且王子一次只能选择上下左右四个方向的任一方向前进一格。那么你可以有很多途径到达终点,但是如何用算法求得所需行走的最短次数呢?我以为需要注意以下几点:

  1. 创建一个能进行宽搜的节点待查队列。
  2. 标记那些已经搜索过的节点,以便下次不会重复搜索。
  3. 记录每个节点与根节点(起始点)的距离。

综上,我们算法要做的就是以起始点(王子最初所在点)为中心点,设定该中心点到起始点距离为0,检测起始点是否就是终点,如果不是,向上下左右四个方向搜索与起始点相邻的节点同时将其标记为已搜索,并将符合条件的节点加入到待查队列中,记录这些子节点与起始点的距离。之后再以待查队列的中任一节点为中心点(具体为哪个节点,这要看刚刚是按何种顺序将子节点添加到待查队列当中),检测它是否就是终点,如果不是,继续往当前中心点的四周搜索并标记为已搜索,添加符合条件的节点进入待查列表,并记录这些子节点与起始点的距离。如此循环直到检测到当前中心点为终点为止。然后输出记录的距离。

个人比较形象的感觉就是,这有点像波的扩散过程,每个子波都产生新的波面。而每个子节点都产生新的子节点集。(这种说法仅仅为了便于理解,但是并不准确)

大致步骤如下:

  1. 将起始节点加入队列queue[0]中,并标记为已搜索,以起始点为中心点,将该点距离设为0。
  2. 进行出队列操作,判断当前点是否为终点,如果是,终止算法,如果不是进行步骤3。
  3. 搜索中心点上下左右四个方向的相邻节点,并标记为已搜索。(如果不进行标记,那么会有什么后果?明显,会对任意一个点进行无休止的搜索)
  4. 将符合条件的节点添加到待查队列中,记录节点与起始点的距离。
    5,循环步骤2~5。

算法分析至此,接下来是代码时间。程序猿们请准备好你们的武器。
我们先做些约定:

  1. 以’S’表示起始点,以’E’表示终点
  2. ‘.’表示路径可通过,’*’表示障碍
#include <iostream>
#include <stdio.h>
#define N 100
/*******************
N用于确定地图矩阵的最大行或列
*******************/

using namespace std;

struct queue
{
     char c;
     int x, y, dis;
};
/*******************
定义一个结构类型,x,y指示坐标位置,dis指示节点到起始点的距离
c用于存储迷宫字符
*******************/

int main()
{
    struct queue que[N*N];
    char maze[N][N] = {0};
    int r, c, sx, sy;   //r指示迷宫矩阵的行数,c指示迷宫矩阵的列数,sx,sy用于存储起始点的坐标
    cin >> r >> c;
    for(int i = 0; i < r; i++)
    {
         for(int j = 0; j < c; j++)
         {
              cin >> maze[i][j]; //先从屏幕上读取迷宫矩阵
              if(maze[i][j] == 'S')  //起始点
              {
                   sy = i;
                   sx = j;
              }
         }
    }
    int vis[N][N] = {0};  //用于记录是否搜索过某一节点,为0表示没被搜索过,为1表示已搜索过
    int dx[] = {1, 0, 0, -1};
    int dy[] = {0, 1, -1, 0};
    /******************
    dx[],dy[]数组比较关键,通过这两个数组可以实现对中心点四周的节点的搜索
    ******************/
    
    int front = -1, rear = -1;
    que[++rear].c = maze[sy][sx];
    que[rear].x = sx;
    que[rear].y = sy;
    que[rear].dis = 0;
    vis[sx][sy] = 1;
    /**********************
    这一块实现上述的步骤1
    **********************/
    
    while(front < rear)
    {
         ++front;
         if(que[front].c == 'E')
          {
               cout << que[front].dis << endl;
               return 0;
          }
         else
         {
              for(int i = 0; i < 4; i++)
              {
                   int tx = que[front].x + dx[i];
                   int ty = que[front].y + dy[i];
                   if(tx >= 0 && tx < c && ty >= 0 && ty < r
                      && !vis[ty][tx] && maze[ty][tx] != '*')
                      /**********************
                      此处保证了搜索节点一定位于迷宫矩阵内同时目标节点不是碍,
                      且目标节点未被搜索过
                      **********************/
                   {
                        que[++rear].c = maze[ty][tx];
                        que[rear].x = tx;
                        que[rear].y = ty;
                        que[rear].dis = que[front].dis + 1;
                        vis[ty][tx] = 1;
                   }
              }
         }
    }
    cout << "you can't pass.\n";
    return 0;
}

以下是我测试过的两个简单的输入与输出:

输入:
2 5
….E
S….
输出:5

输入:
2 5
..*.E
S.*..
输出:you can't pass.
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,585评论 18 139
  • 前言 其实读完斯坦福的这本《互联网大规模数据挖掘》,让我感觉到,什么是人工智能?人工智能就是更高层次的数据挖掘。机...
    我偏笑_NSNirvana阅读 12,494评论 1 23
  • 归去来兮。 1.1 说明 本篇为《挑战程序设计竞赛(第2版)》[http://www.ituring.com.cn...
    尤汐Yogy阅读 14,270评论 0 160
  • 这个系列的第六个主题,主要谈一些搜索引擎相关的常见技术。 1995年是搜索引擎商业公司发展的重要起点,《浅谈推荐系...
    我偏笑_NSNirvana阅读 6,596评论 3 24
  • 1 序 2016年6月25日夜,帝都,天下着大雨,拖着行李箱和同学在校门口照了最后一张合照,搬离寝室打车去了提前租...
    RichardJieChen阅读 5,071评论 0 12