数据结构::迷宫(一)--栈的一个应用
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)我是将迷宫存在了当前工程下,用相对路径来读取迷宫地图
*读者可以自己用二维数组实现一把,读取迷宫可以采取其他的方式
*这里最重要的是理解了走迷宫的思想,所以实现方法可以多种多样嘛
(在这张图片里我们用“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)我是将迷宫存在了当前工程下,用相对路径来读取迷宫地图
*读者可以自己用二维数组实现一把,读取迷宫可以采取其他的方式
*这里最重要的是理解了走迷宫的思想,所以实现方法可以多种多样嘛
相关文章推荐
- 数据结构::迷宫(二)--栈的一个应用(求迷宫最短路径)
- 简易计算器设计中的一个数据结构问题——Ada应用实例之二
- 复习数据结构---纯C编译栈及栈应用(迷宫)
- 数据结构-stack栈应用(走迷宫)
- 菜鸟QiFang学数据结构系列(一):一维数组的高级应用--一个可容纳超多位数的求N!的程序!
- 数据结构::如何计算后缀表达式--栈的一个小应用
- 数据结构的应用------------迷宫求解
- 数据结构学习之多重链表的一个应用场景
- hdoj 1272 小希的迷宫 又一个并查集的简单应用
- 【数据结构】简单迷宫:假设只有一个出口
- 数据结构应用:利用栈破解迷宫游戏
- 数据结构的应用——使用栈实现任意迷宫的求解
- 重温数据结构-栈的应用:进制转换,括号匹配检测,行编辑,迷宫求解,求表达式的值
- 数据结构:栈的应用(迷宫的求解)
- 数据结构(ZKNU OJ) 迷宫(栈的应用)解题报告
- SDUT1130数据结构上机测试1:顺序表的应用(上一个代码太蠢)
- 《数据结构》(C语言版)——栈的应用举例-迷宫求解
- (数据结构)图的应用,一个简单的学校地图.包含的内容:图的最短路径算法 和 图的深度优先遍历算法
- 数据结构应用实例#栈#迷宫寻路
- set,bitset 的一个应用实例——数据结构和比较算法