Java与算法之(12) - 老鼠再闯迷宫(广度优先算法)
贪吃的小老鼠又回来了,这次有什么新的办法吃到奶酪呢?
规则不变,只能上下左右在格子内移动。
因为上次的深度优先算法让老鼠走了不少冤枉路,这次老鼠带来了帮手探路鼠。探路鼠的使用规则如下:
小老鼠按右、下、左、上的顺序向身边四个格子尝试放出探路鼠,如果遇到猫、出边界、已经有探路鼠存在的格子则放弃。
每只探路鼠都有唯一的顺序号,第一只从1开始,每放成功一只序号递增1。
老鼠探路完成后,找出当前未行动过的顺序号最小的探路鼠重复老鼠的工作,即尝试向右、下、左、上四个格子放出探路鼠。
用图来解释一下,第一步,小老鼠放出两只探路鼠,如下:
老鼠行动完成,按规则是1号探路鼠行动。由于地形所限,1号尝试了右、下、左、上四个方向后,只成功放出了3号。
1号完成后,轮到2号行动,也只成功放出一只,即4号
据此规则不难推算出,接下来依次是:
3号放出5号
4号放出6号
5号放出7号
6号放出8号
7号放出9、10号
8号放出11号
9号放出12号
如下图:
注意12号探路鼠首先发现了奶酪,这时它向上一级即9号汇报,9号向7号汇报。。。,12->9->7->5->3->1->老鼠,可以计算出最少的步数是6。
上面的探路过程即广度优先搜索(Breadth First Search, BFS),与深度优先搜索的一条路走到黑不同,每到一个新的位置都向四个方向分别探索,找出每一个分支,并对每一个分支继续探索。
用程序来描绘这一过程,首先需要把迷宫“数字化“,如下图:
这样就可以用一个二维数组存储迷宫:
- int width = 5; //迷宫宽度
- int height = 4; //迷宫高度
- int[][] maze = new int[width][height];
- maze[2][0] = 1;
- maze[1][2] = 1;
- maze[2][2] = 1;
- maze[4][1] = 1;
用一个同样大小的二维数组标记已经放了探路鼠的点
- int[][] mark = new int[width][height];
- mark[0][0] = 1;
每个“探路鼠”需要知道自己所在位置(坐标),自己的上一级是谁。为了方便,还用它记录了到达该位置需要的步数。用一个类来表示:
- static class Trace {
- public Trace(int x, int y, int father, int step) {
- this.x = x;
- this.y = y;
- this.father = father;
- this.step = step;
- }
- private int x;
- private int y;
- private int father;
- private int step;
- public int getX() {
- return x;
- }
- public void setX(int x) {
- this.x = x;
- }
- public int getY() {
- return y;
- }
- public void setY(int y) {
- this.y = y;
- }
- public int getFather() {
- return father;
- }
- public void setFather(int father) {
- this.father = father;
- }
- public int getStep() {
- return step;
- }
- public void setStep(int step) {
- this.step = step;
- }
- @Override
- public String toString() {
- return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
- }
- }
完整代码如下:
- import org.apache.commons.lang3.builder.ToStringBuilder;
- import org.apache.commons.lang3.builder.ToStringStyle;
- import java.util.ArrayList;
- import java.util.List;
- /**
- * 老鼠走迷宫 BFS算法
- * Created by autfish on 2016/9/5.
- */
- public class BfsRatMaze {
- int min = Integer.MAX_VALUE;
- int endX = 4; //目标点横坐标
- int endY = 2; //目标点纵坐标
- int width = 5; //迷宫宽度
- int height = 4; //迷宫高度
- int[][] maze;
- int[][] mark;
- static class Trace {
- public Trace(int x, int y, int father, int step) {
- this.x = x;
- this.y = y;
- this.father = father;
- this.step = step;
- }
- private int x;
- private int y;
- private int father;
- private int step;
- public int getX() {
- return x;
- }
- public void setX(int x) {
- this.x = x;
- }
- public int getY() {
- return y;
- }
- public void setY(int y) {
- this.y = y;
- }
- public int getFather() {
- return father;
- }
- public void setFather(int father) {
- this.father = father;
- }
- public int getStep() {
- return step;
- }
- public void setStep(int step) {
- this.step = step;
- }
- @Override
- public String toString() {
- return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);
- }
- }
- public void bfs() {
- int[][] next = new int[][] { //按右->下->左->上的顺序尝试
- {1, 0},
- {0, 1},
- {-1, 0},
- {0, -1}
- };
- int head = 0, tail = 1;
- int startX = 0, startY = 0;
- int nextX, nextY;
- List<Trace> traces = new ArrayList<>();
- traces.add(head, new Trace(startX, startY, -1, 0));
- mark[startX][startY] = 1;
- int flag = 0;
- while(head < tail) {
- for(int k = 0; k <= 3; k++) {
- nextX = traces.get(head).getX() + next[k][0];
- nextY = traces.get(head).getY() + next[k][1];
- if(nextX < 0 || nextX >= width || nextY < 0 || nextY >= height) { //超出边界
- continue;
- }
- //没有障碍且没有探索过, 则把当前位置标记为未探索点
- if(maze[nextX][nextY] == 0 && mark[nextX][nextY] == 0) {
- this.mark[nextX][nextY] = 1;
- traces.add(tail, new Trace(nextX, nextY, head, traces.get(head).getStep() + 1));
- tail++;
- }
- if(nextX == endX && nextY == endY) {
- flag = 1;
- break;
- }
- }
- if(flag == 1)
- break;
- //一个点的四个方向探索完成, 取编号最小的一个未探索点
- head++;
- }
- Trace end = traces.get(tail - 1);
- int father = end.getFather();
- System.out.println("共" + end.getStep() + "步");
- StringBuilder path = new StringBuilder();
- path.insert(0, "->[" + end.getX() + "," + end.getY() + "]");
- while(father >= 0) {
- Trace prev = traces.get(father);
- father = prev.getFather();
- if(father > -1)
- path.insert(0, "->[" + prev.getX() + "," + prev.getY() + "]");
- else
- path.insert(0, "[" + prev.getX() + "," + prev.getY() + "]");
- }
- System.out.println(path.toString());
- }
- public void initMaze() {
- this.maze = new int[width][height];
- this.mark = new int[width][height];
- this.maze[2][0] = 1;
- this.maze[1][2] = 1;
- this.maze[2][2] = 1;
- this.maze[4][1] = 1;
- this.mark[0][0] = 1;
- //打印迷宫 _表示可通行 *表示障碍 !表示目标
- for(int y = 0; y < height; y++) {
- for(int x = 0; x < width; x++) {
- if(x == endX && y == endY) {
- System.out.print("! ");
- } else if(this.maze[x][y] == 1) {
- System.out.print("* ");
- } else {
- System.out.print("_ ");
- }
- }
- System.out.println();
- }
- System.out.println();
- }
- public static void main(String[] args) {
- BfsRatMaze b = new BfsRatMaze();
- b.initMaze();
- b.bfs();
- }
- }
运行结果:
- _ _ * _ _
- _ _ _ _ *
- _ * * _ !
- _ _ _ _ _
- 共6步
- [0,0]->[1,0]->[1,1]->[2,1]->[3,1]->[3,2]->[4,2]
用深度优先搜索的程序见:
Java与算法之(12) - 老鼠再闯迷宫(广度优先算法)的更多相关文章
- [转载]java面试中经常会被问到的一些算法的问题
Java面试中经常会被问到的一些算法的问题,而大部分算法的理论及思想,我们曾经都能倒背如流,并且也能用开发语言来实现过, 可是很多由于可能在项目开发中应用的比较少,久而久之就很容易被忘记了,在此我分享 ...
- 【SSL1455&1456】 电子老鼠闯迷宫 & 骑士游行
考点概况: 广搜板子 题面: \[\Large\text{电子老鼠闯迷宫}\] \[Time~Limit:1000MS~~Memory~Limit:65536K\] Description 如下图12 ...
- Java Web 深入分析(12) JVM(2) 垃圾收集与内存分配
前言 java的内存分配和垃圾回收往往是影响系统性能和并发能力的主要因素,虚拟机提供许多的参数就是为了根据不同环境和请教下进行调优,没有最好的调优也没有固定的调优.需要我们深入的去了解jvm的各个垃圾 ...
- Java 中级 学习笔记 2 JVM GC 垃圾回收与算法
前言 在上一节的学习中,已经了解到了关于JVM 内存相关的内容,比如JVM 内存的划分,以及JDK8当中对于元空间的定义,最后就是字符串常量池等基本概念以及容易混淆的内容,我们都已经做过一次总结了.不 ...
- [BFS]电子老鼠闯迷宫
电子老鼠闯迷宫 Description 如下图12×12方格图,找出一条自入口(2,9)到出口(11,8)的最短路径. Input Output Sample Input 12 //迷宫大小 2 9 ...
- C++、Java、Python、Linux、Go、前端、算法,慕课资料分享
C++.Java.Python.Linux.Go.前端.算法,慕课资料分享 微信公众号:大道同行JAVA 如有问题或建议,请后台留言,我会尽力解决你的问题. 前言 又见面了.废话不多说,最近多了一些在 ...
- Java设计模式(十三) 别人再问你设计模式,叫他看这篇文章
原创文章,转载请务注明出处 OOP三大基本特性 封装 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的属性和方法只让可信的类操作,对不可信的进行信息隐藏. 继承 继承是指这样一种能力,它可以使 ...
- 201521123082 《Java程序设计》第12周学习总结
201521123082 <Java程序设计>第12周学习总结 标签(空格分隔): java 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. An ...
- 201521123067 《Java程序设计》第12周学习总结
201521123067 <Java程序设计>第12周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 书面作业 将Student对 ...
随机推荐
- 晒下我在2017年所阅读的JavaScript书单
前言 学习是一个持续不断的过程,在互联网技术里畅游的程序猿们,对学习的渴望更是难以穷尽.2017即将逝去,2018已经漏出曙光,回顾这一年,在学习的路上收获还是颇丰的,下面就晒一晒2017年我所学习的 ...
- AI 系列 总目录
AI 系列 答应了园区大牛 张善友 要写AI 的系列博客,所以开始了AI 系列之旅. 一.四大平台系列(百度AI.阿里ET.腾讯.讯飞) 1.百度篇 (1) 百度OCR文字识别-身份证识别 (2) 基 ...
- 【正则表达式】匹配时间格式:hh:mm或h:m
^(0?[0-9]|1[0-9]|2[0-3]):(0?[0-9]|[1-5][0-9])$
- XMPP协议的基本理解
即时通讯技术简介 即时通讯技术(IM)支持用户在线实时交谈.如果要发送一条信息,用户需要打开一个小窗口,以便让用户及其朋友在其中输入信息并让交谈双方都看到交谈的内容.大多数常用的即时通讯发送程序都会提 ...
- Spring事务管理总结
本文是对慕课网上"搞定SSM开发"路径的系列课程的总结,详细的项目文档和课程总结放在github上了.点击查看 本文对应慕课网上课程Spring事务管理,详情可查看:点我 1: 概 ...
- arcgis api for js热力图优化篇-不依赖地图服务
前面我写过一篇文章,介绍如何实现arcgis api的热力图效果,但是依赖arcgis server发布的地图服务来获取热力图的数据源.实际应用中,很多业务数据来源数据库,并不一定是从地图服务来获取的 ...
- springBoot系列教程08:拦截器(Interceptor)的使用
拦截器intercprot 和 过滤器 Filter 其实作用类似 在最开始接触java 使用struts2的时候,里面都是filter 后来springmvc时就用interceptor 没太在意 ...
- thinkinginjava学习笔记08_接口
抽象类和抽象方法 抽象方法是指没有具体实现的方法,仅仅有方法的声明和没有方法体:使用abstract关键字定义一个抽象方法:包含抽象方法的类成为抽象类,如果一个类中包含抽象方法则必须使用abstrac ...
- Python中将函数作为另一个函数的参数传入并调用
在Python中,函数本身也是对象,所以可以将函数作为参数传入另一函数并进行调用 在旧版本中,可以使用apply(function, *args, **kwargs)进行调用,但是在新版本中已经移除, ...
- MySQL优化一 简绍
优化方面: 存储层:数据表”存储引擎”选取.字段类型选取.逆范式(3范式) 设计层:索引.分区/分表 架构层:分布式部署(主从模式/共享) sql语句层:结果一样的情况下,要选择效率高.速度快.节省资 ...