简述

本算法摘选自啊哈磊所著的《啊哈!算法》第四章第三节的题目——BFS算法再次解救小哈。文中代码使用C语言编写,博主通过阅读和理解,重新由Java代码实现了一遍,以此来理解BFS算法。关于解救小哈的迷宫题目,可以参看前一篇博文。

啊哈算法之解救小哈:https://www.cnblogs.com/captainad/p/11039967.html

解法思路

本篇博文,我们将利用广度优先搜索(Breadth First Search, BFS)(也称宽度优先搜索)算法来解决这个问题。

假设有如下迷宫地图:

最开始,我们从(1, 1)的位置开始,一步之内能够到达的点有(1, 2)和(2, 1)。

但是小哈并不在这两个点上,那我们只能通过(1,  2)和(2, 1)这两个点继续往下走。比如我们现在走到了(1, 2)这个点,之后又有可能到那个新的点呢?只有(2, 2)了,因为(1, 3)这个点是墙,无法通行的。那再看看通过(2, 1)这个点再走一步能够到达哪些点?可以到达(2, 2)和(3, 1)。此时会发现(2, 2)这个点既可以从(1, 2)到达也可以从(2, 1)到达,并且都只使用了2步。为了防止一个点多次被走到,我们使用桶(book数组)来记录是否被走到过。

此时我们可以走到的点就全部走到了,有(2, 2)和(3, 1),但是小哈并不在这两个点上,那我们得继续尝试往下找,看看通过(2, 2)和(3, 1)这两个点还能到达哪些新的没有走过的点。通过(2, 2)这个点我们可以到达(2, 3)和(3, 2),通过(3, 1)这个点我们可以到达(3, 2)和(4, 1)。现在3步可以到达的点有(2, 3)、(3, 2)和(4, 1),依旧没有到达目标位置即小哈所在的点,那么我们需要重复刚才的方法,直到到达小哈所在的点为止。

总结上面的算法,我们可以用一个队列来模拟这个过程

我们从一个点开始,并先将这个点入队,开始扩展时,分别往上下左右四个方向走一步,如果这一步没有被走过且没有遇到墙,则将这一步的点位入队,继续扩展,当对上一个点位扩展完了之后,这个点位对于当前来说已经没有用了,就将这个点位出队,接下来,在刚刚那个点位扩展出来的这几个点位的基础上按照上面的方式继续向下探索,这样一来,我们可以记录下从起点开始每走一步就能到达的点位,如果没有到达目标点位,则需要继续探索,否则停止。

我们从上面的图示结合队列来进心一番运算,来明白使用队列解决此问题的奥秘。

首先我们从(1, 1)这个点开始,将这个点入队,然后开始往上下左右四个方向(可使用数组来表示方向)进行探索,发现可以向右到达(1, 2)以及向下到达(2, 1),并且每扩展到一个可行的点位就入队列,向左和向上就出界了,于是从(1, 1)这个点走一步能够扩张到的点已经全部找齐,此时队列中就有(1, 1)、(1, 2)和(2, 1),对(1, 1)这个点位扩展完毕之后,这个点就没有存在的意义了,所以将此点出队,于是队首就是(1, 2),接着我们开始从(1, 2)这个点开始向四个方向探索,重复上面的步骤即可,直到找到目标为位置为止。(可以结合画图来理解这个步骤和意图)

从上面的分析我们可以得出,从某个点开始探索,每次往四个方向探索,且只走一步,遇到可行的点位就入队列,探索完一番之后,将队首的那个点位出队,因为这个点位就探索完了,接着往下一个点位探索,即新队首的点位,如此往复层层递进,直到探索到目标位置。

代码实现

 /**
* @Project: dailypro
* @PackageName: com.captainad.algorithm
* @Author: Captain&D
* @Websit: https://www.cnblogs.com/captainad/
* @DateTime: 2019/6/19 15:31.
* @Description: 使用广度优先搜索算法解救小哈
*/
public class SaveBfs { /**
* 自定义节点,表示地图上的某个点位及其相关信息
*/
static class BfsNode { /** 横坐标 */
int x; /** 纵坐标 */
int y; /** 父节点在队列中的编号,输出路径时使用 */
int f; /** 步数 */
int s;
} /**
* 自定义队列,用来记录探索点位的步骤
*/
static class BfsQueue {
BfsNode[] data = new BfsNode[2500];
int head;
int tail;
public BfsQueue(int head, int tail) {
this.head = head;
this.tail = tail;
}
} public static void main(String[] args) { // 定义一个表示走方向的数组,分别时向右、向下、向左、向上
int[][] next = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; // 定义一个桶,用来标记已经路过的点
int[][] book = new int[50][50]; // 初始化地图矩形大小,初始化地图数据,略
int m = 5, n = 4;
int[][] map = new int[50][50]; // 测试用数据
map[0][0] = 0;
map[0][1] = 0;
map[0][2] = 1;
map[0][3] = 0;
map[1][0] = 0;
map[1][1] = 0;
map[1][2] = 0;
map[1][3] = 0;
map[2][0] = 0;
map[2][1] = 0;
map[2][2] = 1;
map[2][3] = 0;
map[3][0] = 0;
map[3][1] = 1;
map[3][2] = 0;
map[3][3] = 0;
map[4][0] = 0;
map[4][1] = 0;
map[4][2] = 0;
map[4][3] = 1; // 初始化入口坐标
int startx = 0, starty = 0; // 目标点位
int q = 3, p = 2; // 队列初始化
BfsQueue queue = new BfsQueue(0, 0); // 往队列插入迷宫的入口坐标,同时在桶中标记
BfsNode startNode = new BfsNode();
startNode.x = startx;
startNode.y = starty;
startNode.f = 0;
startNode.s = 0;
queue.data[queue.tail] = startNode;
queue.tail++; // 用于标记是否达到目的点位,1表示到达
int flag = 0; // 当队列不为空的时候就一直循环
while(queue.head < queue.tail) { // 下一个点的坐标
int tx, ty; // 枚举四个方向
for(int k = 0; k < 4; k++) { // 计算下一个点的坐标
tx = queue.data[queue.head].x + next[k][0];
ty = queue.data[queue.head].y + next[k][1]; // 判断下一个点是否越界
if(tx < 0 || tx >= m || ty < 0 || ty >= n) {
continue;
} // 判断是否障碍物或者已经路过的点,否则加入到队列中
if(map[tx][ty] == 0 && book[tx][ty] == 0) {
// 注意:广度优先搜索里每个点只入队一次,和深度搜索不同,这里不需要将book桶清理
// 把经过的点进行标记
book[tx][ty] = 1; // 插入新的点加到队列中
BfsNode newNode = new BfsNode();
newNode.x = tx;
newNode.y = ty;
// 因为当前点是从head处扩展出来的,所以为了记录路径,需要记录上一个点的位置
newNode.f = queue.head;
// 步数是之前步数+1
newNode.s = queue.data[queue.head].s + 1;
queue.data[queue.tail] = newNode;
queue.tail++;
} // 如果目标点位已经到达,则停止扩展,任务结束,退出循环
if(tx == q && ty == p) { // 标记为已结束
flag = 1;
break;
}
} // 结束则退出
if(flag == 1) {
break;
} // 当扩展完一个点之后,这个点就没有记录的必要了,进行出队,再对后面的点进行扩展,
queue.head++;
} // 打印最后一个节点的步数即可,注意tail指针指向的是最后一个有内容的节点的下一个节点的位置
System.out.println(String.format("解救小哈最少需要%d步。", queue.data[queue.tail - 1].s)); } }

参考资料

1、《啊哈!算法》/ 啊哈磊著. 人民邮电出版社

啊哈算法之宽搜BFS解救小哈的更多相关文章

  1. hdu 1548 A strange lift 宽搜bfs+优先队列

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1548 There is a strange lift.The lift can stop can at ...

  2. 关于宽搜BFS广度优先搜索的那点事

    以前一直知道深搜是一个递归栈,广搜是队列,FIFO先进先出LILO后进后出啥的.DFS是以深度作为第一关键词,即当碰到岔道口时总是先选择其中的一条岔路前进,而不管其他岔路,直到碰到死胡同时才返回岔道口 ...

  3. 利用深搜和宽搜两种算法解决TreeView控件加载文件的问题。

    利用TreeView控件加载文件,必须遍历处所有的文件和文件夹. 深搜算法用到了递归. using System; using System.Collections.Generic; using Sy ...

  4. POJ1426 Find The Multiple (宽搜思想)

    Find The Multiple Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 24768   Accepted: 102 ...

  5. Colorado Potato Beetle(CF的某道) & 鬼畜宽搜

    题意: 一个人在一张大图上走,给你路径与起点,求他走出的矩形面积并.(大概这个意思自行百度标题... SOL: 与其说这是一道图论题不如说是一道生动活泼的STL-vector教学.... 离散化宽搜, ...

  6. 【宽搜】Vijos P1360 八数码问题

    题目链接: https://vijos.org/p/1360 题目大意: 3x3格子上放1~8数字,一个空位,每次空位可与上下左右交换,固定终止布局,求输入的起始布局需要几步到达终止布局 题目思路: ...

  7. 【宽搜】Vijos P1206 CoVH之再破难关

    题目链接: https://vijos.org/p/1206 题目大意: 给你开始和结束两张4x4的01图,每次操作只能够交换相邻的两个格子(有公共边),问最少的操作步数. 题目思路: [搜索] 这题 ...

  8. 【宽搜】Vijos P1051 送给圣诞夜的极光

    题目链接: https://vijos.org/p/1051 题目大意: 给一张‘-’和‘#’的图,规定曼哈顿距离小于等于2的‘#’属于同一图案,求图案数.[曼哈顿距离:对于A(x1,y1)和B(x2 ...

  9. 【宽搜】【并查集】Vijos P1015 十字绣

    题目链接: https://vijos.org/p/1015 题目大意: n*m的网格,线只能在网格的顶点处才能从布的一面穿到另一面.每一段线都覆盖一个单位网格的两条对角线之一,而在绣的过程中,一针中 ...

随机推荐

  1. Ubuntu环境下对拍

    何为对拍 假设我在考场上写了一个能过样例的算法.然后它也能过大样例但是我觉得有些担心某些细节会出错,或者是它连大样例都过不了但是大样例过大无法肉眼差错,这个时候我们就需要对拍了. 所谓对拍,就是对着拍 ...

  2. bzoj 5281 [Usaco2018 Open]Talent Show——0/1分数规划

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=5281 把分子乘1000,就能在整数里做了. 这种水题也花了这么久…… #include< ...

  3. c# namespace不能和class的name 相同

    比如namespace A, 内部Class A, 那么调用class A的方法只能通过A.A.XXX来访问. 或者说实例化一个class A,  A a = new A(); // compile ...

  4. IIS PHP的Loaded Configuration File为空解决[转]

    在Windows Server 2003上,IIS配置支持PHP,发现PHP扩展未加载,phpinfo()查看,显示 Configuration File (php.ini) Path (none) ...

  5. day11会话管理

    会话管理入门 2.1 生活中会话 我: 小张,你会跳小苹果码? 小张: 会,怎么了? 我: 公司年会上要表演节目,你教教我把 小张:没问题,一顿饭而已. 我: OK. ........ 在这次生活中的 ...

  6. day1 java基础回顾-内省

    为什么要学内省? 开发框架时,经常需要使用java对象的属性来封装程序的数据,每次都使用反射技术完成此类操作过于麻烦,所以sun公司开发了一套API,专门用于操作java对象的属性. 内省是用于操作j ...

  7. OVN简单部署

    部署OVN实验环境 网络拓扑 ### Central节点 # cat ifcfg-eth0 TYPE=Ethernet BOOTPROTO=static DEFROUTE=yes PEERDNS=ye ...

  8. Solr6.7 学习笔记(01) -- 目录结构

    Solr解压后的目录结构 --contrib: Solr的一些扩展 --analysis-extras: 包含一些文本分析组件及其依赖 --clustering: 包含一个用于集群搜索结果的引擎 -- ...

  9. IT兄弟连 JavaWeb教程 过滤器3

    过滤器案例:字符编码过滤器 在JavaWeb程序开发中,由于Web容器内部所使用编码格式并不支持中文字符集,所以,处理浏览器请求中的中文数据就会出现乱码现象. 图3  无字符编码过滤器 从上图可以看出 ...

  10. Python读写Excel表格

    最近在做一些数据处理和计算的工作,因为数据是以.CSV格式保存的,因此刚开始直接用Excel来处理. 但是做着做着发现重复的劳动,其实并没有多大的意义,于是就想着写个小工具帮着处理. 以前正好在一本书 ...