#include<iostream>
using namespace std;
class T //定义描述迷宫中当前位置的结构类型
{
public:
int x; //x代表当前位置的行坐标
int y; //y代表当前位置的列坐标
int dir; //0:无效,1:东,2:南,3:西,4:北
};
class LinkNode //链表结点
{
friend class Stack;
public:
T data;
LinkNode next;
};
class Stack
{
private:
LinkNode top; //指向第一个结点的栈顶指针
public:
Stack(); //构造函数,置空栈
~Stack(); //析构函数
void Push(T e); //把元素data压入栈中
T Pop(); //使栈顶元素出栈
T GetPop(); //取出栈顶元素
void Clear(); //把栈清空
bool empty(); //判断栈是否为空,如果为空则返回1,否则返回0
};
Stack::Stack() //构造函数,置空栈
{
top=NULL;
}
Stack::~Stack() //析构函数
{
}
void Stack::Push(T e) //把元素x压入栈中
{
LinkNode P;
P=new LinkNode;
P->data=e;
P->next=top;
top=P;
}
T Stack::Pop() //使栈顶元素出栈
{
T Temp;
LinkNode P;
P=top;
top=top->next;
Temp=P->data;
delete P;
return Temp;
}
T Stack::GetPop() //取出栈顶元素
{
return top->data;
}
void Stack::Clear() //把栈清空
{
top=NULL;
}
bool Stack::empty() //判断栈是否为空,如果为空则返回1,否则返回0
{
if(top==NULL) return 1;
else return 0;
}
int move[4][2]={{0,1},{1,0},{0,-1},{-1,0}}; //定义当前位置移动的4个方向
bool Mazepath(int maze,int m,int n);
//寻找迷宫maze中从(0,0)到(m,n)的路径
//到则返回true,否则返回false
void PrintPath(Stack p); //输出迷宫的路径
void Restore(int maze,int m,int n); //恢复迷宫
int GetMaze(int &m,int &n); //获取迷宫
//返回存取迷宫的二维指针
int main()
{
int m=0,n=0; //定义迷宫的长和宽
int maze; //定义二维指针存取迷宫
maze=GetMaze(m,n); //调用GetMaze(int &m,int &n)函数,得到迷宫
if(Mazepath(maze,m,n)) //调用Mazepath(int maze,int m,int n)函数获取路径
cout<<"迷宫路径探索成功!\n";
else cout<<"路径不存在!\n";
return 0;
}
int GetMaze(int &m,int &n)//返回存取迷宫的二维指针
{
int maze; //定义二维指针存取迷宫
int i=0,j=0;
cout<<"请输入迷宫的长和宽:";
int a,b;cin>>a>>b; //输入迷宫的长和宽
cout<<"请输入迷宫内容:\n";
m=a;
n=b; //m,n分别代表迷宫的行数和列数
maze=new int [m+2]; //申请长度等于行数加2的二级指针
for(i= 0;i<m+2;i++) //申请每个二维指针的空间
{
maze[i]=new int[n+2];
}
for(i=1;i<=m;i++) //输入迷宫的内容,0代表可通,1代表不通
for(j=1;j<=n;j++)
cin>>maze[i][j];
for(i=0;i<m+2;i++)
maze[i][0]=maze[i][n+1]=1;
for(i=0;i<n+2;i++)
maze[0][i]=maze[m+1][i]=1;
return maze; //返回存贮迷宫的二维指针maze
};
bool Mazepath(int maze,int m,int n)//寻找迷宫maze中从(0,0)到(m,n)的路径
//到则返回true,否则返回false
{
Stack q,p; //定义栈p、q,分别存探索迷宫的过程和存储路径
T Temp1,Temp2;
int x,y,loop;
Temp1x=1;
Temp1y=1;
qPush(Temp1); //将入口位置入栈
pPush(Temp1);
maze[1][1]=-1; //标志入口位置已到达过
while(!qempty()) //栈q非空,则反复探索
{
Temp2=qGetPop(); //获取栈顶元素
if(!(pGetPop()x==qGetPop()x&&pGetPop()y==qGetPop()y))
pPush(Temp2);
//如果有新位置入栈,则把上一个探索的位置存入栈p
for(loop=0;loop<4;loop++) //探索当前位置的4个相邻位置
{
x=Temp2x+move[loop][0]; //计算出新位置x位置值
y=Temp2y+move[loop][1]; //计算出新位置y位置值
if(maze[x][y]==0) //判断新位置是否可达
{
Temp1x=x;
Temp1y=y;
maze[x][y]=-1; //标志新位置已到达过
qPush(Temp1); //新位置入栈
}
if((x==(m))&&(y==(n))) //成功到达出口
{
Temp1x=m;
Temp1y=n;
Temp1dir=0;
pPush(Temp1); //把最后一个位置入栈
PrintPath(p); //输出路径
Restore(maze,m,n); //恢复路径
return 1; //表示成功找到路径
}
}
if(pGetPop()x==qGetPop()x&&pGetPop()y==qGetPop()y)
//如果没有新位置入栈,则返回到上一个位置
{
pPop();
qPop();
}
}
return 0; //表示查找失败,即迷宫无路经
}
void PrintPath(Stack p) //输出路径
{
cout<<"迷宫的路径为\n";
cout<<"括号内的内容分别表示为(行坐标,列坐标,数字化方向,方向)\n";
Stack t; //定义一个栈,按从入口到出口存取路径
int a,b;
T data;
LinkNode temp;
temp=new LinkNode; //申请空间
temp->data=pPop(); //取栈p的顶点元素,即第一个位置
tPush(temp->data); //第一个位置入栈t
delete temp; //释放空间
while(!pempty()) //栈p非空,则反复转移
{
temp=new LinkNode;
temp->data=pPop(); //获取下一个位置
//得到行走方向
a=tGetPop()x-temp->datax; //行坐标方向
b=tGetPop()y-temp->datay; //列坐标方向
if(a==1) temp->datadir=1; //方向向下,用1表示
else if(b==1) temp->datadir=2; //方向向右,用2表示
else if(a==-1) temp->datadir=3; //方向向上,用3表示
else if(b==-1) temp->datadir=4; //方向向左,用4表示
tPush(temp->data); //把新位置入栈
delete temp;
}
//输出路径,包括行坐标,列坐标,下一个位置方向
while(!tempty()) //栈非空,继续输出
{
data=tPop();
cout<<'('<<datax<<','<<datay<<','<<datadir<<","; //输出行坐标,列坐标
switch(datadir) //输出相应的方向
{
case 1:cout<<"↓)\n";break;
case 2:cout<<"→)\n";break;
case 3:cout<<"↑)\n";break;
case 4:cout<<"←)\n";break;
case 0:cout<<")\n";break;
}
}
}
void Restore(int maze,int m,int n) //恢复迷宫
{
int i,j;
for(i=0;i<m+2;i++) //遍历指针
for(j=0;j<n+2;j++)
{
if(maze[i][j]==-1) //恢复探索过位置,即把-1恢复为0
maze[i][j]=0;
}
}
示例输出:
测试1:
请输入迷宫的长和宽:5 5
请输入迷宫内容:
0 1 1 0 0
0 0 1 1 0
1 0 0 1 1
1 0 0 1 0
1 1 0 0 0
迷宫的路径为
括号内的内容分别表示为(行坐标,列坐标,数字化方向,方向)
(1,1,1,↓)
(2,1,2,→)
(2,2,1,↓)
(3,2,1,↓)
(4,2,2,→)
(4,3,1,↓)
(5,3,2,→)
(5,4,2,→)
(5,5,0,)
迷宫路径探索成功!
测试2:
请输入迷宫的长和宽:9 8
请输入迷宫内容:
0 0 1 0 0 0 1 0
0 0 1 0 0 0 1 0
0 0 0 0 1 1 0 1
0 1 1 1 0 0 1 0
0 0 0 1 0 0 0 0
0 1 0 0 0 1 0 1
0 1 1 1 1 0 0 1
1 1 0 0 0 1 0 1
1 1 0 0 0 0 0 0
迷宫的路径为
括号内的内容分别表示为(行坐标,列坐标,数字化方向,方向)
(1,1,1,↓)
(2,1,1,↓)
(3,1,1,↓)
(4,1,1,↓)
(5,1,2,→)
(5,2,2,→)
(5,3,1,↓)
(6,3,2,→)
(6,4,2,→)
(6,5,3,↑)
(5,5,2,→)
(5,6,2,→)
(5,7,1,↓)
(6,7,1,↓)
(7,7,1,↓)
(8,7,1,↓)
(9,7,2,→)
(9,8,0,)
迷宫路径探索成功!
该算法是不稳定的,其时空复杂度不仅和m,n有关,还和mg[][]的具体数值有关。
最坏情况下:每个点都试探过才走到终点。此时时间复杂度为:(mn-1)4,(其中4为4个方向),空间复杂度mn2,(其中mn为存储迷宫图空间,mn为栈空间);
再好情况下:一次试探过就走到终点。此时时间复杂度为:(min(m,n)-1),空间复杂度mn;
所以:
该算法时间复杂度为:[(mn-1)4+(min(m,n)-1)]/2,约为2×m×n
空间复杂度为3mn/2
本程序的前提是将迷宫保存在一个二维数组里,可走的地方为0,不可走的地方为1。由于采用回朔算法,不使用递归,所以首先应该建立一个栈来保存路径,路径是用一个一个点来表示的,也就是说栈中保存的是一系列点的列表。
栈节点类型说明:
struct StackNode
{
POINT Point;
struct StackNode Next, Prev;//双向链表形式
};
栈结构用一个类(CPointStack)实现,声明如下:
class CPointStack
{
public:
void ClearStack();//清空栈
void InitStack();//初始化栈
bool Pop(POINT &pt);//d出顶端元素,并给出该点的坐标,返回是否d出成功
bool Push(POINT pt);//将pt点的信息压入栈,返回是否压入成功
CPointStack();
virtual ~CPointStack();
protected:
StackNode m_psnTop;//栈顶指针
StackNode m_snBottom;//栈底,不保存点信息
};
以下为成员函数实现,都是一些数据结构的 *** 作,应该没什么好说的:
//构造时初始化栈
CPointStack::CPointStack()
{
InitStack();
}
//析构时清空栈
CPointStack::~CPointStack()
{
ClearStack();
}
//将点压入栈(成功返回true,失败返回false)
bool CPointStack::Push(POINT pt)
{
StackNode NewNode = new StackNode;
//是否返回true主要看这里申请内存是否成功
if(!NewNode)
return false;
NewNode->Point = pt;
NewNode->Prev = m_psnTop;
NewNode->Next = NULL;
m_psnTop->Next = NewNode;
m_psnTop = NewNode;
return true;
}
//将点d出栈(成功返回true,栈为空则返回false)
bool CPointStack::Pop(POINT &pt)
{
if(m_psnTop == &m_snBottom)//是否返回true主要看这里栈是否为空
return false;
StackNode p;
p = m_psnTop;
m_psnTop = m_psnTop->Prev;
pt = p->Point;
delete p;
m_psnTop->Next = NULL;
return true;
}
//初始化栈
void CPointStack::InitStack()
{
m_psnTop = &m_snBottom;
m_snBottomNext = NULL;
m_snBottomPrev = NULL;
}
//清空栈
void CPointStack::ClearStack()
{
StackNode p;
while(m_psnTop != &m_snBottom)
{
p = m_psnTop;
m_psnTop = m_psnTop->Prev;
delete p;
}
}
接下来设计怎样走出这个迷宫,也就是迷宫算法的主体部分。再次用一个类实现。
在设计这个类时,我考虑到以下几点:
1、迷宫入口和出口的坐标
2、保存迷宫的2维数组
3、获得路径的函数
但是实际设计时由于没有考虑太多问题,只是以设计迷宫算法和解决一个具体迷宫为目的,所以没有考虑动态大小的迷宫,而是将迷宫大小定为601×401。而且在设计迷宫算法后没有将路径顺序输出而是直接在原图中标识(在保存迷宫数据的表中设置经过的点为2而将死路点设置为3)。
这样迷宫类(CGoMaze)的声明为:
class CGoMaze
{
public:
void Go();//寻找路径的函数
POINT m_ptIn;//入口坐标
POINT m_ptOut;//出口坐标
BYTE m_btArrMaze[401][601];//保存迷宫的二维表
CGoMaze();
virtual ~CGoMaze();
};
最后让我们来看一下CGoMaze::Go()这个函数:
我们模仿人走迷宫时的思路,设置一个当前点,一个目标点(下一个要走的点)。初始情况下当前点为入口,终止条件为当前点为出口,这样,我们的函数大概结构就出来了。
在从入口到出口的过程中程序对当前点的上、下、左、右四个点依次进行判断,当发现任一个方向是未走过的区域时,就将当前点指向那个点进行尝试,同时将当前点入栈并做标记。而当4个方向都不通或已走过时,则为死路,标记当前点为死路并从栈中d出上一个点继续进行尝试,这时因为当前点已被标记为死路,则d出上一个点时就不会重复这条路,达到寻找正确路径的效果。
Go函数的具体实现如下,其实挺简单的^_^:
void CGoMaze::Go()
{
CPointStack psPath;//保存路径的栈
POINT ptCur = m_ptIn, ptNext;//设置当前点为入口
while(ptCurx != m_ptOutx || ptCury != m_ptOuty)//判断是否已经走出
{
ptNext = ptCur;//设置目标点为当前点,便于下面偏移
if(m_btArrMaze[ptCury - 1][ptCurx] == 0)
ptNexty --;//如果上方是空的,则目标点向上偏移
else if(m_btArrMaze[ptCury][ptCurx - 1] == 0)
ptNextx --;//如果左边是空的,则目标点向左偏移
else if(m_btArrMaze[ptCury + 1][ptCurx] == 0)
ptNexty ++;//如果下方是空的,则目标点向下偏移
else if(m_btArrMaze[ptCury][ptCurx + 1] == 0)
ptNextx ++;//如果右边是空的,则目标点向右偏移
else
{//这里是没有路的状态
m_btArrMaze[ptCury][ptCurx] = 3;//标记为死路
psPathPop(ptCur);//d出上一次的点
continue;//继续循环
}
//如果有路的话会执行到这里
m_btArrMaze[ptCury][ptCurx] = 2;//标记当前点为路径上的点
psPathPush(ptCur);//当前点压入栈中
ptCur = ptNext;//移动当前点
}
}
其实这个部分可以将栈设置为CGoMaze类的成员,然后在Go函数里加上:
psPathClearStack();
这样就可以用Timer来演示走迷宫的过程了
/ 迷宫矩阵
1 1 1 1 1 1 1 1 1 1
1 0 0 0 0 1 0 0 0 1
1 1 1 0 0 1 0 1 0 1
1 0 0 0 1 0 0 0 0 1
1 0 0 0 0 1 0 1 0 1
1 1 0 1 0 0 0 0 0 1
1 0 0 1 1 1 0 1 1 1
1 1 0 0 0 1 0 0 0 1
1 1 1 1 1 1 1 1 1 1
/
#include<stdioh>
#define m 7
#define n 8
void path()
{
int maze[m+2][n+2] ;
int move[4][2]={ {0,-1},{-1,0},{0,1},{1,0} };
int s[54][3];
int top=0;
int i,j,k,f=0;
int g,h,p;
for(i=0;i<m+2;++i)
for(j=0;j<n+2;++j)
scanf("%d",&maze[i][j]);
maze[1][1]=2;
s[top][0]=1;
s[top][1]=1;
s[top][2]=0;
++top;
while(top!=0&&f==0)
{
--top;
i=s[top][0];
j=s[top][1];
k=s[top][2];
while(k<4)
{
g=i+move[k][0];
h=j+move[k][1];
if(g==m&&h==n&&maze[g][h]==0)
{
for(p=0;p<top;++p)
printf("%3d,%d\n",s[p][0],s[p][1]);
printf("%3d,%d\n",i,j);
printf("%3d,%d\n",m,n);
f=1;
}//if
if(maze[g][h]==0)
{
maze[g][h]=2;
s[top][0]=i;
s[top][1]=j;
s[top][2]=k;
++top;
i=g;
j=h;
k=0;
}//if
k=k+1;
}//while
}//while
if(f==0)
printf("no path\n");
}//pathvoid main()
{
path();
}
这个迷宫的路径不是唯一的,因此从不同方向开始试探执行结果也可能会不唯一。我写的是参考书上的,共有八个方向可以试探。
栈解决迷宫主要的几个问题:
1迷宫的存储
2栈的设计
3试探方向
4不重复到达某点,即不陷入死循环
如果对算法有什么疑问,或是我的回答有错误的地方,可以Hi我。
#define LINES 9 // 定义行数
#define COLS 8 // 定义列数
#include <stdioh>
#include <stdlibh>
#include <malloch>
typedef struct{
int line;
int col;
}MOVE; // 定义试探方向的结构体
MOVE to[8] = {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}}; // 定义数组,存放8个试探方向
typedef struct{ // 行、列、方向构成的三元组
int x;
int y;
int d;
}DataType;
typedef struct node{
DataType element;
struct node next;
}StackNode,LinkStack;
LinkStack InitStack(LinkStack s); // 栈初始化
LinkStack PushStack(LinkStack,DataType ); // 入栈函数
LinkStack PopStack(LinkStack,DataType ); // 出栈函数
int EmptyStack(LinkStack s); // 判定栈空
int path(int maze[][COLS+2]); // 打印路径
void printpath(LinkStack s,DataType t);
int main( void )
{
int i,j;
int maze[LINES+2][COLS+2] = // 定义存放迷宫的数组并初始化
{1,1,1,1,1,1,1,1,1,1,
1,0,0,1,0,0,0,1,0,1,
1,0,0,1,0,0,0,1,0,1,
1,0,0,0,0,1,1,0,1,1,
1,0,1,1,1,0,0,1,0,1,
1,0,0,0,1,0,0,0,0,1,
1,0,1,0,0,0,1,0,1,1,
1,0,1,1,1,1,0,0,1,1,
1,1,1,0,0,0,1,0,1,1,
1,1,1,0,0,0,0,0,0,1,
1,1,1,1,1,1,1,1,1,1
};
for(i=1;i<LINES+1;i++)
{
for(j=1;j<COLS+1;j++){
if( 0 == maze[i][j] ){
printf("□");
}
else{
printf("■");
}
}
printf("\n");
}
if( path(maze) ){
printf("找到一条路径\n");
for(i=1;i<LINES+1;i++)
{
for(j=1;j<COLS+1;j++)
{
if( 0 == maze[i][j] ){
printf("□");
}
else if( 1 == maze[i][j]){
printf("■");
}
else{
printf("☆");
}
}
printf("\n");
}
}
else{
printf("迷宫无路径\n");
}
return 0;
}
LinkStack InitStack(LinkStack s)
{
return NULL;
}
LinkStack PushStack(LinkStack s,DataType t)
{
StackNode p;
p = NULL;
p = (StackNode )malloc(sizeof(StackNode));
if(!p){
printf("申请结点失败\n");
exit(1);
}
p->element = t;
p->next = s;
s = p;
return s;
}
LinkStack PopStack(LinkStack s,DataType t)
{
StackNode p = NULL;
if( EmptyStack(s) ){
printf("栈空\n");
return NULL;
}
p = s;
t = p->element;
s = s->next;
free(p);
p = NULL;
return s;
}
int EmptyStack(LinkStack s)
{
if( NULL == s ){
return 1;
}
return 0;
}
int path(int maze[][COLS+2])
{
int i,j,x1,y1,v;
DataType temp;
LinkStack top;
tempx = 1;
tempy = 1;
tempd = -1;
top = InitStack(top);
top = PushStack(top,&temp);
while( !EmptyStack(top) )
{
top = PopStack(top,&temp);
// 入口为(1,1),从0方向开始试探
x1 = tempx;
y1 = tempy;
v = tempd + 1;
while( v < 8 )
{
i = x1 + to[v]line;
j = y1 + to[v]col;
if( 0 == maze[i][j] ){ // 到达新点
tempx = x1;
tempy = y1;
tempd = v;
top = PushStack(top,&temp); // 坐标及方向入栈
x1 = i;
y1 = j;
maze[x1][y1] = -1; // 对已经到过的点做标记
if( x1 == LINES && y1 == COLS ){ // 到达出口
printpath(top,&temp); // 打印路径
return 1;
}
else{
v = 0;
}
}
else{
v++;
}
}
}
return 0;
}
void printpath(LinkStack s,DataType t)
{
while( !EmptyStack(s) )
{
printf("(%d, %d, %d)\n",s->elementx,s->elementy,s->elementd);
s = PopStack(s,t);
}
}
你这个题意表述得不是很完整。首先,这个迷宫的出口和入口坐标分别是哪个?也就是迷宫的起点和终点在哪?第二,行走的规则是什么?是只能横竖走?还是也可以斜着走。
然后像这类的寻路有一种很好的算法叫A寻路算法,你可以自己百度一下。大致的思路是从起点开始,维护两个表。第一个表是下一步可以到达的格子属性表,这个表中每个格子至少要有权值(也就是预计离目标远近的值)和父节点2个属性,而且必须按权值顺序排序。另外一个表是已经寻过路的格子。讲起来有点复杂,但做起来不是太难。百度一下吧。
以上就是关于用c++写一个迷宫程序全部的内容,包括:用c++写一个迷宫程序、C语言迷宫问题,求该算法的时间和空间的复杂度。迷宫的路径已经定义好,求出路的算法。、C++设计一个迷宫并走出来等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)