本文适合于对迷宫问题已有初步研究,或阅读代码能力较强的人.
因此,如果你对迷宫问题一无所知,请参考其他更详细的资料.
迷宫问题,是一个对栈(Stack)典型应用的例子之一.
假如,有如下10X10的迷宫(0代表通路,1代表障碍),我们需要用写程序来找出迷宫的出口.
1 1 1 1 1 1 1 1 1 1
1 1 1 0 1 1 1 0 1 1
0 0 0 0 1 0 0 0 1 1
1 1 0 1 1 0 1 0 0 1
1 1 0 1 0 0 1 0 1 1
1 1 0 1 1 1 1 0 0 1
1 1 0 0 0 0 0 0 1 1
1 1 0 1 0 1 1 0 1 1
1 1 0 1 0 1 1 0 1 1
1 1 1 1 1 1 1 0 1 1
那么,我们可以通过两种方式完成.
方式一:通过利用栈FILO(First In Last Out)的特性
核心代码
/*
*函数说明:通过栈来进行迷宫求解
*参数说明:
* Maze:迷宫地图数组
* sz:迷宫大小
* entry:迷宫入口点
* path:用于寻找迷宫出口的栈
*返回值:找到出口返回true,没找到返回false.
*/
bool FindMazePath(int *Maze,size_t sz,Pos &entry,stack& path){
//将入口压栈
path.push(entry);
//如果栈不为空
while(!path.empty()){
//获取栈顶元素,即上一次走的路径
Pos cur = path.top();
//将其标记为已走过
Maze[cur.x*sz+cur.y] = 3;
//找到出口
if(sz-1==cur.x){
return true;
}
Pos next = cur;
//下一步,向右移动
next.x += 1;
if(CheckIsAccess(Maze,sz,next)){
//可以向右移动,将当前步入栈
path.push(next);
continue;
}
next = cur;
//下一步,向左移动
next.x -= 1;
if(CheckIsAccess(Maze,sz,next)){
//可以向左移动,入栈
path.push(next);
continue;
}
//下一步,向上移动
next = cur;
next.y += 1;
if(CheckIsAccess(Maze,sz,next)){
//可以向上移动
path.push(next);
continue;
}
next = cur;
//向下移动
next.y -= 1;
if(CheckIsAccess(Maze,sz,next)){
//可以向下移动
path.push(next);
continue;
}
//上、下、左、右都不能走
path.pop();
}
return false;
}
方式二:通过递归
核心代码
/*
*函数说明:根据递归寻找迷宫出口
*参数说明
* Maze:迷宫地图
* sz:迷宫大小
* entry:迷宫入口
* path:用来判断是否存在出口的栈
*返回值:无(如果存在出口,栈为空;如果不存在出口,栈中存在起点坐标)
*/
void FindMazePathR(int *Maze,size_t sz,Pos &entry,stack & path){
//将入口压栈
path.push(entry);
Pos cur = entry;
//将已走过的路标记为3
Maze[cur.x*sz+cur.y] = 3;
//找到出口,直接返回
if(sz-1==entry.x){
//将起点坐标弹出
path.pop();
return ;
}
Pos next = cur;
//右
next.x += 1;
if(CheckIsAccess(Maze,sz,next)){
//以当前位置为起点,递归进行下一步
FindMazePathR(Maze,sz,next,path);
}
next = cur;
//左
next.x -= 1;
if(CheckIsAccess(Maze,sz,next)){
FindMazePathR(Maze,sz,next,path);
}
//上
next = cur;
next.y += 1;
if(CheckIsAccess(Maze,sz,next)){
FindMazePathR(Maze,sz,next,path);
}
//下
next = cur;
next.y -= 1;
if(CheckIsAccess(Maze,sz,next)){
FindMazePathR(Maze,sz,next,path);
}
path.pop();
}
最后,附上整个程序的完整代码(代码量较少,声明与实现我就不分文件了)
迷宫问题求解完整代码
//相关函数的声明与实现
#ifndef __MAZE_H__
#define __MAZE_H__
#include
#include
#include
#include
namespace Maze{
using namespace std;
//迷宫大小
static const int N = 10;
//迷宫地图文件名
static const char *const FILENAME = "MazeMap.txt";
//坐标
struct Pos{
int x; //横坐标(本质是数组arr[i][j]的j)
int y; //纵坐标(本质是数组arr[i][j]的i)
};
/*
函数说明:从文件中获取迷宫地图
参数说明:
Maze:迷宫地图数组
sz:迷宫大小
返回值:无
*/
void GetMaze(int *Maze,size_t sz){
FILE *fp = fopen(FILENAME,"r");
//打开失败
if(NULL==fp){
//输出错误信息
perror(FILENAME);
//结束程序
exit(1);
}
//将文件中的迷宫地图读入Maze数组内
for(size_t i=0; i=0 && cur.x=0 && cur.y& path){
//将入口压栈
path.push(entry);
//如果栈不为空
while(!path.empty()){
//获取栈顶元素,即上一次走的路径
Pos cur = path.top();
//将其标记为已走过
Maze[cur.x*sz+cur.y] = 3;
//找到出口
if(sz-1==cur.x){
return true;
}
Pos next = cur;
//下一步,向右移动
next.x += 1;
if(CheckIsAccess(Maze,sz,next)){
//可以向右移动,将当前步入栈
path.push(next);
continue;
}
next = cur;
//下一步,向左移动
next.x -= 1;
if(CheckIsAccess(Maze,sz,next)){
//可以向左移动,入栈
path.push(next);
continue;
}
//下一步,向上移动
next = cur;
next.y += 1;
if(CheckIsAccess(Maze,sz,next)){
//可以向上移动
path.push(next);
continue;
}
next = cur;
//向下移动
next.y -= 1;
if(CheckIsAccess(Maze,sz,next)){
//可以向下移动
path.push(next);
continue;
}
//上、下、左、右都不能走
path.pop();
}
return false;
}
/*
*函数说明:根据递归寻找迷宫出口
*参数说明
* Maze:迷宫地图
* sz:迷宫大小
* entry:迷宫入口
* path:用来判断是否存在出口的栈
*返回值:无(如果存在出口,栈为空;如果不存在出口,栈中存在起点坐标)
*/
void FindMazePathR(int *Maze,size_t sz,Pos &entry,stack & path){
//将入口压栈
path.push(entry);
Pos cur = entry;
//将已走过的路标记为3
Maze[cur.x*sz+cur.y] = 3;
//找到出口,直接返回
if(sz-1==entry.x){
//将起点坐标弹出
path.pop();
return ;
}
Pos next = cur;
//右
next.x += 1;
if(CheckIsAccess(Maze,sz,next)){
//以当前位置为起点,递归进行下一步
FindMazePathR(Maze,sz,next,path);
}
next = cur;
//左
next.x -= 1;
if(CheckIsAccess(Maze,sz,next)){
FindMazePathR(Maze,sz,next,path);
}
//上
next = cur;
next.y += 1;
if(CheckIsAccess(Maze,sz,next)){
FindMazePathR(Maze,sz,next,path);
}
//下
next = cur;
next.y -= 1;
if(CheckIsAccess(Maze,sz,next)){
FindMazePathR(Maze,sz,next,path);
}
path.pop();
}
}
#endif
迷宫求解测试代码
#include"Maze.h"
using namespace Maze;
void MazeTest(){
int arr[N][N]; //迷宫地图
Pos entry = {2,0}; //起点坐标
stack path; //栈
GetMaze((int *)arr,N); //将文件中迷宫导入到arr数组中
PrintMaze((int *)arr,N);//打印迷宫
FindMazePath((int *)arr,N,entry,path);//找迷宫出口
cout<
总结:
1.利用栈去寻找迷宫出口,栈内最终会保存从入口到出口的所有路径.
2.利用递归去寻找迷宫出口,传进去的栈仅仅只是用来判断迷宫是否有出口,
3.利用递归去寻找出口时,因为递归的特性,将会遍历完迷宫内的所有路径.
最后,还有一个问题:如果一个迷宫存在多条路径可以到达出口,那么如何得到迷宫到出口的最短路径???
有机会的话,我将会在下篇文章讨论此事.
附上工程文件:http://pan.baidu.com/s/1gfoNrLD