标签:产生 技术分享 接下来 依次 左右 选择 ios family 结构体
迷宫问题求解是一个非常经典的算法问题,该问题要求程序能根据用户的输入的长和宽去初始化迷宫,接着给用户提供两个选择,用户可以选择在迷宫里手动或自动生成指定个数的障碍,接着程序会自动找到一条能够从入口走到出口的路径,并且输出该路径
下面开始分析,因为迷宫是由多个点组成的,那么要实现上述功能,我们可以定义一个结构体去存放每个点的横纵坐标去表示位置,还需要用到一个二维数组去存放迷宫,在迷宫里有三种状态,0代表通路,1代表障碍,2代表已经走过的路,那么可以将指定的值一一赋给数组中每个元素,迷宫里的点是需要用坐标去表示的,那么为了方便,可以将二维数组下标(横坐标或纵坐标)为0的点(及迷宫的外围)都设置为1(障碍),为了防止找路时走出去,走过的路(走过的每个坐标)需要用到一个栈去存放每个点,栈其实就是一段内存,有两个指针一开始都指向数组的头地址(也能理解为栈底),接着随着数据的存放,栈顶指针会跟着存放的数据移动,栈底指针不变,那么根据这两个指针就可以随时确定栈顶元素的位置和栈的状态(空或满),因为栈这种特殊的数据结构可以保证后进入的数据先出来,所以栈在迷宫找路是可以模拟出走过的每一步以及遇到障碍时往后退的上一步位置(也就是后进入的栈顶元素),所以可以创建一个栈,里面的指针类型为定义的结构体类型
2.详细设计
首先根据用户输入的长和宽m和n用new去开辟大小为m+2(算上围墙)大小的内存,在m+2的基础上再用new开放n+2(算上围墙)大小的内存,就开辟了二维数组空间,返回的值(数组的地址)用一个二重指针去存放:具体代码如下(该函数在function.h)
int **initmaze(int &m,int &n)//用0或1输入代表迷宫,0代表通路
{
cout << "请输入迷宫的长和宽" << endl;
cout << "长度:";
cin >> m;
cout << "宽度:";
cin >> n;
cout << "请创建迷宫,0代表通路,1代表此路不通" << endl;
int **maze = new int *[n + 2];//用new,避免缓冲区溢出
for (int i = 0; i <= n + 1; i++)//创建迷宫储存空间,为迷宫设置障碍
{
maze[i] = new int[m+2];//长度加2,为迷宫周围设置障碍
}
for (int i = 0; i <= n + 1; i++)
{
maze[i][0] = maze[i][m + 1] = 1;//为每一行的第一个和最后一个元素初始化为1,为迷宫的障碍
}
for (int i = 0; i <= m + 1; i++)
{
maze[0][i] = maze[n + 1][i] = 1;//为每一列的第一个和最后一个元素初始化值为1,为迷宫障碍
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
maze[i][j]=0;
}
}
return maze;
}
在stack.h这个头文件定义栈这种数据结构,定义结构体position用来存放迷宫里点的坐标,代码如下:
struct position
{
int row;//行偏移
int col;//列偏移
};//定义一个结构体,用来存放位置坐标
typedef position selemtype;
typedef struct//定义栈结构为结构体栈,用来存放迷宫中某一点的横纵坐标
{
selemtype *top;//栈顶指针
selemtype *base;//栈底指针
int stacksize;
}sqstack;
定义initstack函数初始化栈,具体操作用new开辟一段内存,将这段内存的首地址赋值给top和base(两个指向position结构体类型的指针)用来表示存放路径的栈:
bool initstack(sqstack &s)//顺序栈的初始化
{
s.base = new selemtype[MAXSIZE];//为顺序栈分配空间
if (!s.base) return 0;//储存分配失败
s.top = s.base;
s.stacksize = MAXSIZE;
return 1;
}
定义一些函数去对栈进行操作
该函数将走过的点存放到栈里面,将栈顶指针重新向上移一位,指向栈顶元素
bool push(sqstack &s, selemtype e)//插入元素e为栈顶元素
{
if (s.top - s.base == s.stacksize) exit(0);//空间已满,分配失败
*s.top = e;//将值赋给栈顶指针
s.top++;//栈顶指针向上移1位
return 1;
}
该函数删除栈顶元素,即将栈顶指针向下移一位(在迷宫里表示遇到障碍退路):
bool pop(sqstack &s, selemtype &e)//删除栈顶元素
{
if (empty(s)) return 0;//栈为空,不能删除
e = *(s.top-1);//将栈顶指针减一,将栈顶元素赋给e
s.top--;
return 1;
}
该函数取得栈顶元素,及取回top指针指向的元素(在迷宫里表示退到上一位置,因为栈顶元素就是对应上一步)
该函数判断栈是否为满,根据指针位置判断
bool full(sqstack &s)//判断栈是否满了
{
if (s.top - s.base == 0)return 0;
else return 1;
}
该函数判断栈是否为空,根据指针位置判断
bool empty(sqstack &s)//判断栈是否为空
{
if (s.base == s.top)return 1;
else return 0;
}
该函数输出栈中元素,表示输出迷宫从入口到出口的路径
void stackprint(sqstack s)//输出栈中元素
{
selemtype *q;
for (q = s.base; q !=s.top; q++)
{
cout << "("<<q->row << "," <<q->col<<")"<< endl;
}
}
以上对栈的操作都放入stack.h头文件中
上述实现了迷宫需要用到的一些数据类型和数据结构
接下来分析如何实现迷宫问题
比如以下是一个迷宫,长和宽为4,旁边代表围墙,入口为(1,1),
首先要生成障碍,有两个选择,手动和自动生成,其实没区别,只需要把障碍对应的坐标设置为1即可,自动就是在指定的长宽内去生成随机数,一下为自动和手动的代码实现
int **randmaze(int **p, int &m, int &n)//随机产生障碍
{
time_t t=time(0);
srand(t);
int num, x, y;
cout << "请输入障碍物的个数"<<endl;
cin >> num;
cout << "ok,正在为你生出障碍物....请稍等" << endl;
for (int i = 0; i < num; i++)
{
x = rand() % m;//生成的随机数在指定迷宫长度的范围内,不包括出口
y = rand() % n;//生成随机数在指定迷宫宽度的范围内
p[x][y] = 1;//设置障碍
}
return p;
}
int **inputmaze(int **p, int &m, int &n)//手动输入障碍
{
int num, x, y;
cout << "请输入设置障碍物的个数" << endl;
cin >> num;
cout << "请输入障碍物的位置,行和列" << endl;
for (int i = 0; i < num; i++)
{
cout << "行:";
cin >> x;
cout << "列:";
cin >> y;
while ((x<=1 || x >=n) && (y <= 1 || y >=m))//出口入口均不能有障碍
{
cout << "输入不在范围内" << endl;
cout << "行:";
cin >> x;
cout << "列:";
cin >> y;
}
p[x][y] = 1;
}
return p;
}
设置好障碍后就开始找路,每个点都有四个选择,及向上下左右,那么可以用结构体去存放相应的偏移量,比如右偏移为行为0,列偏移为1
position offset[4];//结构体数组,存放位置偏移量
offset[0].row = 0; offset[0].col = 1;//右偏移
offset[1].row = 1; offset[1].col = 0;//下偏移
offset[2].row = 0; offset[2].col = -1;//左偏移
offset[3].row = -1; offset[3].col = 0;//上偏移
这个顺序是有要求的先向右尝试,然后是下,再左,最后上
假设生成的障碍为如下所示
先从入口(1,1)当前位置向右尝试,不行(右为1障碍),于是向下(可以,因为下个位置为0),走到(2,1),将(1,1)设为2(表示来过,防止再次走),然后将(1,1)存入栈,接着(2,2)又有4种尝试,先向右(右(2,2)为0,所以可以走)于是将(1,2)设为2表示走过并存入栈中,接着到(2,2),先向右尝试,(2,3)为1,遇到障碍,再向下尝试,还是障碍,接着上和左都不行,于是只能后退了,于是将上个位置(1,2)从栈中弹出来,将上个位置变成当前位置,接着因为(1,2)已经向右尝试过了不行,于是要从下(结构体下标为1offset[1].row = 1; offset[1].col = 0;//下偏移)开始尝试,向下可以,于是到(1,3),将(1,2)压入栈,下面都是按照上面讲的一一去尝试,直到走到出口,接着将栈输出即可,该找路部分实现代码如下
bool findpath(int **p,int &m,int &n,sqstack &path)//sqstack为存放路径的结构体栈
{
initstack(path);
position offset[4];//结构体数组,存放位置偏移量
offset[0].row = 0; offset[0].col = 1;//右偏移
offset[1].row = 1; offset[1].col = 0;//下偏移
offset[2].row = 0; offset[2].col = -1;//左偏移
offset[3].row = -1; offset[3].col = 0;//上偏移
position here;//用来存放位置
here.row = here.col = 1;
p[1][1] = 2;//入口设为2,防止在回来
int option = 0;//移动方向,根据移动方向进行不同的偏移
int lastoption = 3;
while (here.row != n||here.col != m)//没到出口
{
int r, c;
while (option <= lastoption)//依次走几个方向
{
r = here.row + offset[option].row;
c = here.col + offset[option].col;
if (p[r][c] == 0) break;//如果有通路,则跳出
option++;//否则尝试下一个方向
}
if (option <= lastoption)//如果找到方向
{
push(path, here);//将方向压入结构体栈
here.row = r;
here.col = c;
p[r][c] = 2;//将该点改为2,表示来过
option = 0;//重新设置为0,进行下一次寻路
}
else
{
position next;
if (empty(path))
{
return false;
}
next = gettop(path);//取栈顶元素,及前一点坐标
pop(path, next);//将栈顶弹出
if (here.row == next.row)//如果当前位置和上一位置行相等,说明他们在列上移
{
if (here.col > next.col)
{
option = 1;
}
else
{
option = 3;
}
//option = 2 + next.col - here.col;//下一个移动位置
}
else
{
if (here.row > next.row)
{
option = 2;
}
else
{
return false;
}
//option = 3 + next.row - here.row;//如果当前位置和上一位置列相同,说明当前位置是从行上移过来的
}
here = next;//把栈顶元素(上一位置坐标赋给当前位置,表示退回来)
}
}
return true;
}
具体代码在此贴出
#ifndef _STACK0_
#define _STACK0_
#define MAXSIZE 100
struct position
{
int row;//行偏移
int col;//列偏移
};//定义一个结构体,用来存放位置坐标
typedef position selemtype;
typedef struct//定义栈结构为结构体栈,用来存放迷宫中某一点的横纵坐标
{
selemtype *top;//栈顶指针
selemtype *base;//栈底指针
int stacksize;
}sqstack;
bool full(sqstack &s)//判断栈是否满了
{
if (s.top - s.base == 0)return 0;
else return 1;
}
bool empty(sqstack &s)//判断栈是否为空
{
if (s.base == s.top)return 1;
else return 0;
}
bool initstack(sqstack &s)//顺序栈的初始化
{
s.base = new selemtype[MAXSIZE];//为顺序栈分配空间
if (!s.base) return 0;//储存分配失败
s.top = s.base;
s.stacksize = MAXSIZE;
return 1;
}
bool push(sqstack &s, selemtype e)//插入元素e为栈顶元素
{
if (s.top - s.base == s.stacksize) exit(0);//空间已满,分配失败
*s.top = e;//将值赋给栈顶指针
s.top++;//栈顶指针向上移1位
return 1;
}
bool pop(sqstack &s, selemtype &e)//删除栈顶元素
{
if (empty(s)) return 0;//栈为空,不能删除
e = *(s.top-1);//将栈顶指针减一,将栈顶元素赋给e
s.top--;
return 1;
}
selemtype gettop(sqstack s)//获取栈顶元素的值
{
if (s.top == s.base) exit(0);
//e=*(s.top - 1);//取栈顶元素
return *(s.top-1);
}
void stackprint(sqstack s)//输出栈中元素
{
selemtype *q;
for (q = s.base; q !=s.top; q++)
{
cout << "("<<q->row << "," <<q->col<<")"<< endl;
}
}
#endif;
#ifndef _FUNCTION0_
#define _FUNCTION0_
#include<iomanip>
#include<cstdlib>//用来生成随机数
#include<ctime>
#include "stack.h"
void welcome()
{
cout << "初始化迷宫......" << endl;
}
int **initmaze(int &m,int &n)//用0或1输入代表迷宫,0代表通路
{
cout << "请输入迷宫的长和宽" << endl;
cout << "长度:";
cin >> m;
cout << "宽度:";
cin >> n;
cout << "请创建迷宫,0代表通路,1代表此路不通" << endl;
int **maze = new int *[n + 2];//用new,避免缓冲区溢出
for (int i = 0; i <= n + 1; i++)//创建迷宫储存空间,为迷宫设置障碍
{
maze[i] = new int[m+2];//长度加2,为迷宫周围设置障碍
}
for (int i = 0; i <= n + 1; i++)
{
maze[i][0] = maze[i][m + 1] = 1;//为每一行的第一个和最后一个元素初始化为1,为迷宫的障碍
}
for (int i = 0; i <= m + 1; i++)
{
maze[0][i] = maze[n + 1][i] = 1;//为每一列的第一个和最后一个元素初始化值为1,为迷宫障碍
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
maze[i][j]=0;
}
}
return maze;
}
int **randmaze(int **p, int &m, int &n)//随机产生障碍
{
time_t t=time(0);
srand(t);
int num, x, y;
cout << "请输入障碍物的个数"<<endl;
cin >> num;
cout << "ok,正在为你生出障碍物....请稍等" << endl;
for (int i = 0; i < num; i++)
{
x = rand() % m;//生成的随机数在指定迷宫长度的范围内,不包括出口
y = rand() % n;//生成随机数在指定迷宫宽度的范围内
p[x][y] = 1;//设置障碍
}
return p;
}
int **inputmaze(int **p, int &m, int &n)//手动输入障碍
{
int num, x, y;
cout << "请输入设置障碍物的个数" << endl;
cin >> num;
cout << "请输入障碍物的位置,行和列" << endl;
for (int i = 0; i < num; i++)
{
cout << "行:";
cin >> x;
cout << "列:";
cin >> y;
while ((x<=1 || x >=n) && (y <= 1 || y >=m))//出口入口均不能有障碍
{
cout << "输入不在范围内" << endl;
cout << "行:";
cin >> x;
cout << "列:";
cin >> y;
}
p[x][y] = 1;
}
return p;
}
void printmaze(int **p,int m,int n)
{
for (int i = 0; i <= n + 1; i++)
{
for (int j = 0; j <= m + 1; j++)
{
if (p[i][j] == 1)
{
cout << setw(2) <<"□";
}
if (p[i][j] == 2)
{
cout << setw(2) << "○";
}
if (p[i][j] == 0)
{
cout << setw(2) << "";
}
}
cout << endl;
}
}
void delmaze(int **p, int m, int n)//销毁迷宫
{
for (int i = 0; i <= n + 1; i++)
{
delete[] p[i];
}
delete[] p;
}
bool findpath(int **p,int &m,int &n,sqstack &path)//sqstack为存放路径的结构体栈
{
initstack(path);
position offset[4];//结构体数组,存放位置偏移量
offset[0].row = 0; offset[0].col = 1;//右偏移
offset[1].row = 1; offset[1].col = 0;//下偏移
offset[2].row = 0; offset[2].col = -1;//左偏移
offset[3].row = -1; offset[3].col = 0;//上偏移
position here;//用来存放位置
here.row = here.col = 1;
p[1][1] = 2;//入口设为2,防止在回来
int option = 0;//移动方向,根据移动方向进行不同的偏移
int lastoption = 3;
while (here.row != n||here.col != m)//没到出口
{
int r, c;
while (option <= lastoption)//依次走几个方向
{
r = here.row + offset[option].row;
c = here.col + offset[option].col;
if (p[r][c] == 0) break;//如果有通路,则跳出
option++;//否则尝试下一个方向
}
if (option <= lastoption)//如果找到方向
{
push(path, here);//将方向压入结构体栈
here.row = r;
here.col = c;
p[r][c] = 2;//将该点改为2,表示来过
option = 0;//重新设置为0,进行下一次寻路
}
else
{
position next;
if (empty(path))
{
return false;
}
next = gettop(path);//取栈顶元素,及前一点坐标
pop(path, next);//将栈顶弹出
if (here.row == next.row)//如果当前位置和上一位置行相等,说明他们在列上移
{
if (here.col > next.col)
{
option = 1;
}
else
{
option = 3;
}
//option = 2 + next.col - here.col;//下一个移动位置
}
else
{
if (here.row > next.row)
{
option = 2;
}
else
{
return false;
}
//option = 3 + next.row - here.row;//如果当前位置和上一位置列相同,说明当前位置是从行上移过来的
}
here = next;//把栈顶元素(上一位置坐标赋给当前位置,表示退回来)
}
}
return true;
}
#endif;
#include<iostream>
using namespace std;
#include "function.h"
int main()
{
int m, n,t;
int **maze;
sqstack path;//用来存放路径
welcome();
maze=initmaze(m,n);//初始化函数
printmaze(maze,m,n);//输出初始化的迷宫
cout << "选择1自动生成障碍" << endl;
cout << "选择2手动创建障碍" << endl;
cin >> t;
while (t != 1 && t != 2)
{
cout << "输入错误,请重输" << endl;
cin >> t;
}
switch (t)
{
case 1:maze = randmaze(maze, m, n); break;
case 2:maze = inputmaze(maze, m, n); break;
}
cout << "迷宫如下"<<endl;
printmaze(maze,m,n);
cout << "正在寻找路径" << endl;
if (findpath(maze, m, n,path))
{
stackprint(path);
}
else
{
cout << "没有找到路径" << endl;
}
printmaze(maze,m,n);
delmaze(maze, m, n);//销毁迷宫,释放内存
return 0;
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
标签:产生 技术分享 接下来 依次 左右 选择 ios family 结构体
原文地址:http://www.cnblogs.com/-joker-/p/6828114.html