您的位置:首页 > 理论基础 > 数据结构算法

数据结构::迷宫(一)--栈的一个应用

2016-12-06 11:43 363 查看
【前情描述】:我们先来看一张图片:

  


(在这张图片里我们用“1”来表示墙,用“0”来表示通路。红色方块表示入口点,绿色方块表示出路)

 我们要从迷宫的出口开始走找出路,即红色走到绿色,那么怎么解决这个问题呢?别着急,往下看,听我细细讲解:

【解决迷宫问题】:

方法一:利用栈来解决

   思路分析:

1)我们先要给定一个入口点,从入口点开始出发,即先将入口点进行压栈

2)我们用一个节点来表示栈顶的元素,用此节点来跟踪我们要走的足迹,每当我们走一步,就将节点进行压栈

3)但是我们走的时候有四个方向可以走,所以每走一步就要对这四个方向进行判定,是否可以走通

4)当四个方向都可以走通的时候,就重复以上步骤,当四个方向都走不通的时候,我们就出栈,回溯

5)什么时候结束,当栈为空的时候说明没有通路,回溯到最初的入口点

6)什么时候我们找到了一条通路,就是当我们走的路的行等于迷宫的行的时候

   接下来,我们直接看代码:

#include<iostream>
#include<assert.h>
#include<stack>
using namespace std;
//#define N 10
const size_t N = 10; //尽量使用const

//迷宫中所走的位置表示
struct Pos
{
int _row; //所在位置的行
int _col; //所在位置的列
};

//获取迷宫
void GetMaze(int* maze,size_t n)
{
FILE* fp = fopen("1.txt","r"); //先通过相对路径打开存储迷宫地图的文本
assert(fp); //打开文本是否成功
for(size_t i = 0; i<n; i++)
{
for(size_t j = 0; j<n;)
{
int ret = fgetc(fp);
if((ret == '0')|| (ret == '1'))
{
maze[i*n+j] = ret-'0'; //注意打开的是文本要转化成读出的二进制形式
j++;
}
//如果迷宫地图本身不是n*n的,那么上面的循环就会成为死循环
if(ret == EOF)
{
cout<<"出错!"<<endl;
}
}
}
}
//检查此位置是否可以通
bool CheckIsAccess(int* maze,size_t n,Pos pos)
{
//首先这个位置得合法,其次再是pos这个位置是否为0
if(pos._row>=0 && pos._row<n &&
pos._col>=0 && pos._col<n &&
maze[pos._row*n+pos._col] == 0)
{
return true;
}
return false;
}
//求解迷宫路径
bool GetMazePath(int* maze,size_t n,Pos entry,stack<Pos>& path)
{
assert(maze);
path.push(entry);
Pos cur;
Pos next;
while(!path.empty()) //当栈不为空的时候,才能进行路径的寻找
{
cur = path.top();
maze[cur._row*n+cur._col] = 2; //把走过的这个位置进行标记
next = cur; //在每次进行判断之后,要将next进行重置
if(next._row == n-1)
{
return true; //找到了一条通路
}
//开始对上下左右进行探测
//对上的探测
next._row -= 1;
//检查路是否可以通
if(CheckIsAccess(maze,n,next))
{
path.push(next);
continue;
}
//对下的探测
next = cur;
next._row += 1;
//检查路是否可以通
if(CheckIsAccess(maze,n,next))
{
path.push(next);
continue;
}
//对左的探测
next = cur;
next._col -= 1;
//检查路是否可以通
if(CheckIsAccess(maze,n,next))
{
path.push(next);
continue;
}
//对右的探测
next = cur;
next._col += 1;
//检查路是否可以通
if(CheckIsAccess(maze,n,next))
{
path.push(next);
continue;
}
//到这个位置,说明四个方向都走不通,进行回溯
path.pop(); //直接出栈,即前一个位置,再次上去进行探测
}

return false; //栈为空,没有找到通路
}

//打印迷宫
void Print(int* maze, size_t n)
{
for(size_t i = 0; i<n; i++)
{
for(size_t j = 0; j<n; j++)
{
cout<<maze[i*n+j];
}
cout<<endl;
}
}
int main()
{
int maze

;
stack<Pos> path;
Pos entry = {2,0};
//输出迷宫
GetMaze((int*)maze,N);
cout<<"是否找到迷宫:"<<GetMazePath((int*)maze,N,entry,path)<<endl;
Print((int*) maze,N);
return 0;
}
方法二:利用递归来解决

   
思路分析:递归就是将一个问题不断划分成若干个子问题

1)对于这道题,我们依然从入口点出发,开始从入口点判断,进行四个方向的探测

2)接着我们将要走的下一个位置作为入口点,进行探测,这样不断的递归

3)当我们没有找到通路的时候,开始回变量溯递归,这里我们依然不将压栈的节点不删除,即(path),用栈来记录递归的路径

    我们来看代码:

#include<iostream>
#include<assert.h>
#include<stack>
using namespace std;
#define N 10
//const size_t N = 10;

//迷宫中所走的位置表示
struct Pos
{
/* size_t*/ int _row; //所在位置的行
/*sizt_t*/int _col; //所在位置的列
};

//获取迷宫
void GetMaze(int* maze,size_t n)
{
FILE* fp = fopen("2.txt","r"); //先通过相对路径打开存储迷宫地图的文本
assert(fp); //打开文本是否成功
for(size_t i = 0; i<n; i++)
{
for(size_t j = 0; j<n;)
{
int ret = fgetc(fp);
if((ret == '0')|| (ret == '1'))
{
maze[i*n+j] = ret-'0'; //注意打开的是文本要转化成读出的二进制形式
j++;
}
//如果迷宫地图本身不是n*n的,那么上面的循环就会成为死循环
if(ret == EOF)
{
cout<<"出错!"<<endl;
}
}
}
}
//检查此位置是否可以通
bool CheckIsAccess(int* maze,size_t n,Pos pos)
{
//首先这个位置得合法,其次再是pos这个位置是否为0
if(pos._row>=0 && pos._row<n &&
pos._col>=0 && pos._col<n &&
maze[pos._row*n+pos._col] == 0)
{
return true;
}
return false;
}
//求解迷宫路径
void GetMazePath_r(int* maze,size_t n,Pos entry,stack<Pos>& path)
{
assert(maze);
path.push(entry);
Pos cur;
Pos next;
cur = entry;
maze[cur._row*n+cur._col] = 2; //把走过的这个位置进行标记
next = cur;
if(next._row == n-1)
{
return ; //找到了一条通路
}
//开始对上下左右进行探测
//对上的探测
next = cur;
next._row -= 1;
//检查路是否可以通
if(CheckIsAccess(maze,n,next))
{
GetMazePath_r(maze,n,next,path);
}
//对下的探测
next = cur;
next._row += 1;
//检查路是否可以通
if(CheckIsAccess(maze,n,next))
{
GetMazePath_r(maze,n,next,path);
}
//对左的探测
next = cur;
next._col -= 1;
//检查路是否可以通
if(CheckIsAccess(maze,n,next))
{
GetMazePath_r(maze,n,next,path);
}
//对右的探测
next = cur;
next._col += 1;
//检查路是否可以通
if(CheckIsAccess(maze,n,next))
{
GetMazePath_r(maze,n,next,path);
}
//到这个位置,说明四个方向都走不通,进行回溯
path.pop();
}

//打印迷宫
void Print(int* maze, size_t n)
{
for(size_t i = 0; i<n; i++)
{
for(size_t j = 0; j<n; j++)
{
cout<<maze[i*n+j];
}
cout<<endl;
}
}
int main()
{
int maze

;
stack<Pos> path;
Pos entry = {2,1};
//输出迷宫
GetMaze((int*)maze,N);
GetMazePath_r((int*) maze,N,entry,path);
Print((int*) maze,N);
cout<<"是否找到迷宫:"<<!path.empty()<<endl;
return 0;
}
到这里,相信聪明的你对迷宫问题一定理解了,对栈的应用又有了更深层次的掌握。


【注意】:(关于本文章有些要说明的)

1)我在实现迷宫的时候,首先是构建了二维数组,将上述的迷宫保存下来,但是我实现的时候利用一维数组进行实现。(二维数组实际上也是一维数组)

2)我是将迷宫存在了当前工程下,用相对路径来读取迷宫地图

*读者可以自己用二维数组实现一把,读取迷宫可以采取其他的方式

*这里最重要的是理解了走迷宫的思想,所以实现方法可以多种多样嘛
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: