问题描述

给定一张迷宫地图和一个迷宫入口,然后进入迷宫探索找到一个出口。如下图所示:

该图是一个矩形区域,有一个入口和出口。迷宫内部包含不能穿越的墙壁或者障碍物。这些障碍物沿着行和列放置,与迷宫的边界平行。迷宫的入口在左上角,出口在右下角。

问题分析

  1. 首先要有一张迷宫地图,地图由两部分组成:

(1)一是迷宫中各处的位置坐标,

(2)二是迷宫各位置处的状态信息,即该处是墙还是路

所以,该迷宫地图可由一个二维数组来表示。数组的横纵坐标表示迷宫各处的位置坐标,数组元素表示各位置处的状态信息。

2.在这里,假定:

(1)迷宫地图是m*n的,即二维数组是m行n列的。
(2)在迷宫中用1表示墙,用0表示路。当然,为了便于标识,我们后面还会用其他数字代表更多含义。

因此,迷宫的地图一个刻画如下:

现在我们要找一条从入口到出口的路径。路径是一个由位置组成的序列,每一个位置都没有障碍,并且除入口外,路径上的每一个位置都是前一个位置在东西南北方向上相邻的一个位置。

不过,考虑到边界问题不太好处理。我们做这样的工作,在地图外围加一层围墙,给它全部填上1。这样,在处理各个坐标时,都没有差别了。这样一来便大大简化了我们的工作。

下面我们要编写程序求解这个问题。

程序设计

这次还是采用一个简单的模块化来设计这个程序。那么主要有下面几个模块:

  1. 显示欢迎信息
  2. 初始化工作
  3. 生成地图
  4. 找路
  5. 打印地图和路径

下面我们分别完成这些功能。

显示欢迎信息

这个模块就很简单了,输出一些信息提醒使用者就行,主要是为了增加程序的友好性而设置的。大家根据自己的需要自行发挥。例如我的就很随便了:

void welcome()
{cout << "welcome to RAT IN MAZE" << endl;system("pause");system("cls");
}

初始化工作

这个主要是设置一些全局变量的取值和完成内存的分配,地图的存储还是从堆上分配内存比较好。因为一般来说,考虑到地图可能会很大,这样需要的存储空间就很多了。在这里一并把相关的全局变量给讲解了吧。

typedef struct
{int row;int col;
}POSITION;const POSITION maze_size = { 20 , 60 };int ** const maze = new int*[maze_size.row + 2];stack<POSITION> path;
POSITION offset[4];//direction
  • POSITION结构体
    坐标结构体变量类型,很容易理解,有两个成员变量:x坐标和y坐标。

  • maze_size
    定义地图的大小,实际分配内存的时候,我们还需要考虑地图边界也需要存储空间。总之,我们的地图坐标范围是1 to maze_size。

  • maze
    二位数组,存储地图,分配的时候+2是用来存储边界的。至于const则是约束指针不改变。不过我们的地图数组是根据maze_size大小动态分配的。

  • path
    用来存路径的。

  • offset
    用来设置位置偏移的。比如我们当前位置是(row = 1, col = 1),那么通过row + 1便可往下走,row - 1就是往上走。col + 1往右走,col - 1 往左走。等等。通过坐标加减offset偏移,便可以移动了。

void init()
{//偏移offset[0].row = 0; offset[0].col = 1; //rightoffset[1].row = 1; offset[1].col = 0; //downoffset[2].row = 0; offset[2].col = -1; //leftoffset[3].row = -1; offset[3].col = 0; //up//maze = new int*[maze_size.row + 2];for (int i = 0; i < maze_size.row + 2; i++){maze[i] = new int[maze_size.col + 2];}
}

这个代码就是设置偏移的数值,以及动态分配地图数组了。

生成地图

生成地图还是根据地图尺寸,然后随机设置障碍。不过要注意障碍出现的概率设置得小一点,不然地图一般无解。可以用rand()随机数来做。这一步也要把围墙设置好。

//地图范围1 - maze_size 有围墙
void randomMaze()
{int i, j, rate;for (i = 0; i < maze_size.row + 2; i++){for (j = 0; j < maze_size.col + 2; j++){//设置围墙if ((i == 0) || (i == maze_size.row + 1) || (j == 0) || (j == maze_size.col + 1)){maze[i][j] = 1;}else{rate = rand() % 10+1;if (rate <= 3){maze[i][j] = 1;//随机生成障碍}else{maze[i][j] = 0;}}}}//最后保证起点和终点能走maze[1][1] = maze[maze_size.row][maze_size.col] = 0;
}

找路

这个是整个程序设计的核心功能,没有之一。在写代码之前,我们需要弄明白,到底怎么找路呢?

  1. 首先,把迷宫入口作为当前位置。
  2. 如果当前位置是迷宫出口,那么已经找到一条路径了,程序结束。
  3. 如果当前位置不是出口,则在当前位置放置障碍物,表示这里已经来过,防止下次又重复绕回来。然后检查相邻位置是否能走。
  4. 如果一个相邻位置能走,就移动到这个位置上。然后在新的位置上重新开始寻找出口。如果不能走,就尝试下一个相邻位置。
  5. 如果所有的相邻位置都不能走了,则回退到上一个位置,重新选择上一个位置的其他相邻位置,继续探索。
  6. 如果所有的相邻位置都被探索过了,仍然找不到路径,那说明这个迷宫不存在这样的路径。

例如,下面的地图:

好了,说了这么多,相信大家已经了解得差不多,并且跃跃欲试了。

bool findPath()
{POSITION here; //当前位置here.row = here.col = 1;maze[1][1] = 3; //放置障碍,防止回来int option = 0; //next stepconst int lastOption = 3; //find a pathwhile ( here.row != maze_size.row || here.col != maze_size.col){//not reach the endint r, c;while (option <= lastOption){r = here.row + offset[option].row;c = here.col + offset[option].col;if (maze[r][c] == 0){break;}option++;//next choice}//相邻的位置能走?if (option <= lastOption){path.push(here);here.row = r;here.col = c;maze[r][c] = 3; //走过了option = 0;}else{if (path.empty()){return false;}//go backmaze[here.row][here.col] = 3; //此路不可通here = path.top();path.pop();option = 0;}}maze[maze_size.row][maze_size.col] = 2;return true;
}

相关代码如上面所示,结合前面的讲解,相信大家也能看懂。

打印地图和路径

这个功能就比较简单了,主要是根据maze的信息,生成相应的地图显示出来给大家直观的看到。对于maze里面存的数值,我们也可以作一个小小的规定:

  • 0
    表示位置可通行。
  • 1
    表示位置有障碍。
  • 2
    表示该位置处在找到的路径上面。
  • 3
    探索过程中放置的障碍物。这个障碍物和1表示的障碍物不同的是,这个障碍我们放置的,和生成地图时的固定障碍物不同。因此还是要区分开来的。

然后打印的时候,遍历maze数组,遇到:

  • 0
    打印空格。
  • 1
    打印*号。
  • 2
    打印#号,并且设置下颜色。
  • 3
    还是打印空格。(或者根据自己的喜好打印另外的符号,这样就可以把探索过的所有位置显示出来。)

最后在打印最终的地图和路径之前,如果找到一条路径。我们还要根据path中的路径,在maze里面设置好相应的值,才做最终的输出:

void setPathOnMaze()
{POSITION pos;while (!path.empty()){pos = path.top();path.pop();maze[pos.row][pos.col] = 2;//路径}
}然后,可以开始输出我们的地图了。

具体代码:

void outputMaze()
{int i, j;for (i = 0; i < maze_size.row + 2; i++){for (j = 0; j < maze_size.col + 2; j++){if (maze[i][j] == 1){cout << "*";}else if ((maze[i][j] == 0) || (maze[i][j] == 3)){cout << " ";}else{HANDLE hOut;hOut = GetStdHandle(STD_OUTPUT_HANDLE);SetConsoleTextAttribute(hOut,FOREGROUND_GREEN | FOREGROUND_INTENSITY);cout << "#";SetConsoleTextAttribute(hOut, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);}}cout << endl;}
}

最终效果

  1. 没有找到路的情况:

  2. 找到了路径:

代码获取

欲获取代码,请关注我们的微信公众号【程序猿声】,在后台回复:rat。即可下载。

【数据结构】10分钟教你用栈求解迷宫老鼠问题超详细教程附C++源代码相关推荐

  1. 【Minecraft】10分钟教你搭建我的世界Java版开服教程【Linux服务器+MCSManager管理面板】

    一个人玩游戏没啥意思,和朋友一块联机呢,距离太远,家庭局域网宽带又没有公网ip,你的朋友没办法与你联机,然而你只需要一台服务器即可搞定了:但是很多用户没没接触过相关的内容,具体的该怎么操作呢?下面我将 ...

  2. uniapp中qrcode生成二维码后传的参数不见了_阿虚教你制作动态二维码,超详细教程!

    这篇教程很早之前就答应几个粉丝要写,拖的有点久了. 内容比较多,先上个目录 阿虚的教程会迟到,但永远不会缺席.hahahahhaha... 一. 先说一下今天要教的内容 ʕ•̫͡•ོʔ•̫͡•ཻʕ•̫ ...

  3. 手把手教你六类网线水晶头接法,超详细教程

    网线线序 六类网线线序一般为白橙.橙.白绿.蓝.白蓝.绿.白棕.棕.如下图所示 工具介绍 工具①是灰色保护套,其作用是保护水晶头,要在剥线前就套上去 ②就是水晶头了 ③的作用是套在两股网线上,旋转,最 ...

  4. 数据结构 10分钟让你掌握经典排序(一)

    数据结构 经典排序算法总结(一) 排序总结 插入排序 直接插入排序 希尔排序 选择排序 简单选择排序 堆排序 推荐阅读 排序总结 经典排序算法是数据结构体系中最重要的内容之一,这一块必须要非常熟练的掌 ...

  5. 如何用计算机自动回复微信,10分钟教你用Python实现微信自动回复功能

    01 前言&&效果展示 相信大家都有忙碌的时候,不可能一直守在微信上及时回复消息.但微信又不能像QQ一样设置自动回复.无妨,今天,我们就来用Python实现微信的自动回复功能吧,并且把 ...

  6. 10分钟学计算机,电脑运行越来越慢?程序员大牛10分钟教你学会电脑瘦身

    原标题:电脑运行越来越慢?程序员大牛10分钟教你学会电脑瘦身 你的电脑是不是越来越慢?这里让程序员大佬用10分钟时间教你学会给电脑软件瘦身,1分钟了解计算机硬件升级.分分钟让你成为别人眼中的计算机大牛 ...

  7. 10分钟教你用python打造贪吃蛇超详细教程

    更多精彩尽在微信公众号[程序猿声] 10分钟教你用python打造贪吃蛇超详细教程 在家闲着没妹子约, 刚好最近又学了一下python,听说pygame挺好玩的.今天就在家研究一下, 弄了个贪吃蛇出来 ...

  8. 10分钟教你用python如何正确把妹

    前言 今天没妹子约,刚好研究一下.如何用神奇的python打造一个把妹神器吧.看完这个,你们就能走向人生巅峰,迎娶白富美啦. 我知道你们想看看效果 当然啦,这只是测试版的效果,真正的版本可比这个厉害多 ...

  9. 10分钟教你用Python玩转微信之抓取好友个性签名制作词云

    10分钟教你用Python玩转微信之抓取好友个性签名制作词云 01 前言+展示 各位小伙伴我又来啦.今天带大家玩点好玩的东西,用Python抓取我们的微信好友个性签名,然后制作词云.怎样,有趣吧~好了 ...

最新文章

  1. kill -9 进程杀不掉,怎么办?
  2. python和java一样吗-三分钟看懂Python和Java的区别
  3. 个人脚本收藏[不断更新 last update 2005-12-10]
  4. Tesseract-OCR 字符识别---样本训练 [转]
  5. opengl加载显示3D模型gltf2类型文件
  6. 大数据WEB阶段Spring框架 AOP面向切面编程(一)
  7. matlab sort descend,详解Matlab中 sort 函数用法
  8. 【CodeForces - 798A】Mike and palindrome (回文串,水题,字符串问题)
  9. linux service start|stop|restart
  10. C++使用system创建进程
  11. 一般熟练盲打需要多久_练习拼音打字练到[盲打]的境界一般需要多久(时间)?...
  12. Axure 如何在页面加载时,设置文本框的内容为当前日期
  13. mysql安装失败原因和解决方法
  14. php 获取指定日期的周几,周始,周末
  15. 塞雷三分钟漫画中国史2
  16. mysql在window上进行备份
  17. html-based script和url-based script区别,使用方式
  18. 前苏联奥数题之12个乒乓球问题解答
  19. 1000个苹果要分到10个箱子中去 两种分析方式
  20. ♥二:TypeScript---变量声明

热门文章

  1. EOF 与 BOF 之分
  2. 实战某小型企业项目系列二:方案设计
  3. 电路——电阻电路的一般分析方法
  4. 如何查自己的ip 物理地址
  5. Tomcat介绍及三种启动方式
  6. 同济大学计算机贴吧,沪上21所高校新老毕业照大集合!杨浦毕业生们,这里有你们的回忆吗?...
  7. 移动设备快速充电(基础篇)之 1.0 当前快充技术的发展现状
  8. Springboot整合自定义页面swaggerUi
  9. 23种设计模式之-----简单工厂(静态工厂)模式(SimpleFactory Pattern)
  10. eNSP三个路由器与三个PC连接