迷宫问题求解利用栈与递归求解出口
Posted 数据结构专题_By_高小调
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了迷宫问题求解利用栈与递归求解出口相关的知识,希望对你有一定的参考价值。
本文适合于对迷宫问题已有初步研究,或阅读代码能力较强的人.
因此,如果你对迷宫问题一无所知,请参考其他更详细的资料.
迷宫问题,是一个对栈(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<Pos>& 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<Pos> & 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<iostream> #include<iomanip> #include<stack> #include<assert.h> 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<sz; ++i){ for(size_t j=0; j<sz;){ //从文件流中获取字符 char tmp = getc(fp); //字符为0或为1时,导入数组 if(tmp==‘0‘||tmp==‘1‘){ Maze[i*sz+j]=tmp -‘0‘; ++j; }else if(EOF==tmp){ //文件已读完,循环还未停止 //说明此处文件中的迷宫地图存在问题 assert(false); return ; } } } //关闭文件 fclose(fp); } /* 函数说明:打印迷宫 参数说明: Maze:迷宫地图数组 sz:迷宫大小 返回值:无 */ void PrintMaze(int *Maze,size_t sz){ cout<<setw(2); for(size_t i=0; i<sz; ++i){ for(size_t j=0; j<sz; ++j){ cout<<Maze[i*sz+j]<<setw(2); } cout<<endl; } } /* 函数说明:检测当前位置是否可以通过 参数说明: Maze:迷宫地图数组 sz:迷宫大小 cur:当前所在位置 返回值:可以通过返回true,不能通过返回false. */ bool CheckIsAccess(int *Maze,size_t sz,Pos cur){ if(cur.x>=0 && cur.x<sz && //行坐标是否越界 cur.y>=0 && cur.y<sz && //列坐标是否越界 Maze[cur.x*sz+cur.y]==0 ){ //所在行列是否可以通过 return true; } return false; } /* 函数说明:通过栈来进行迷宫求解 参数说明: Maze:迷宫地图数组 sz:迷宫大小 entry:迷宫入口点 path:用于寻找迷宫出口的栈 返回值:找到出口返回true,没找到返回false. */ bool FindMazePath(int *Maze,size_t sz,Pos &entry,stack<Pos>& 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<Pos> & 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<Pos> path; //栈 GetMaze((int *)arr,N); //将文件中迷宫导入到arr数组中 PrintMaze((int *)arr,N);//打印迷宫 FindMazePath((int *)arr,N,entry,path);//找迷宫出口 cout<<endl<<endl; //换行处理(使界面更整齐) PrintMaze((int *)arr,N);//打印走过的迷宫 } int main(){ MazeTest(); return 0; }
总结:
1.利用栈去寻找迷宫出口,栈内最终会保存从入口到出口的所有路径.
2.利用递归去寻找迷宫出口,传进去的栈仅仅只是用来判断迷宫是否有出口,
3.利用递归去寻找出口时,因为递归的特性,将会遍历完迷宫内的所有路径.
最后,还有一个问题:如果一个迷宫存在多条路径可以到达出口,那么如何得到迷宫到出口的最短路径???
有机会的话,我将会在下篇文章讨论此事.
附上工程文件:http://pan.baidu.com/s/1gfoNrLD
以上是关于迷宫问题求解利用栈与递归求解出口的主要内容,如果未能解决你的问题,请参考以下文章
汉诺塔(Tower of Hanoi)问题的求解——利用栈与递归