文章目录
一.算法概述
迷宫如下:
0 1 2 3 4 5 6 7 8 9
0 1 1 1 1 1 1 1 1 1 1
1 1 0 1 1 1 0 1 1 1 1
2 1 1 0 1 0 1 1 1 1 1
3 1 0 1 0 0 0 0 0 1 1
4 1 0 1 1 1 0 1 1 1 1
5 1 1 0 0 1 1 0 0 0 1
6 1 0 1 1 0 0 1 1 0 1
7 1 1 1 1 1 1 1 1 1 1
迷宫为8*6方阵
方向由1-8分别为上,右上,右,右下,下,左下,左,左上
1.初始化迷宫并输出
初始化一个长度10,宽度8的二维数组,之所以要把长度宽度都加2,是因为需要一圈围墙来判断迷宫是否出界,并输入迷宫(注:这里我定义横向为长,纵向为宽,注意,在定义二维数组一定要maze[宽+2][长+2],也就是先宽再长)
2.用二维数组记录是否到过迷宫中的点
定义一个二维数组flag[Width+2][Length+2]记录各个点是否到过,是为1,否为0
3.定义(1,1,3)为起始点坐标
将当前坐标,当前方向定义为(1,1,3),即坐标(1,1)方向向右,将flag[1][1]标记为1,初始化一个栈,用于保存路径。定义next_x,next_y保存下一待定坐标,为什么是待定呢?因为不一定能够走到,x,y为当前坐标
4.算法核心:
当下一个点的坐标不是终点坐标时,就用while一直循环遍历
即如果(next_x,next_y)不是(Length,Width),则一直循环
4.1.在进入方向循环之前,进行一次下一步待定坐标的求取
4.2.按照方向direction从1到8循环
4.2.1.求取下一步的待定坐标
4.2.2.判断这个待定坐标,如果下个点没来过(flag为0),并且下个点可以通行(maze为0)执行
{记录下来目前这个点的三个属性并将其压栈,沿着这个方向移动一次,
新到的点标记为来过(flag[y][x]=1)}
4.3.如果4.2.2中没有发生移动,证明走到了死角,保存走到死角之前的坐标,并将这个方向+1,再从这个新的坐标(走到死角之前的x,y坐标和这个+1后的方向)开始继续探索,然后把这个死角前坐标出栈
4.4.如果栈为空,那么迷宫无法出去
5.循环结束,栈中从底到顶即为一条路线
6.输出所有的路线,并求出最短路线
二.具体代码实现:
#include<iostream>
using namespace std;
void intend_move(int x,int y,int &next_x,int &next_y,int direction);//这个是修改下一步待定坐标的函数
void move(int &x,int &y,int &direction);//这个是修改当前坐标的函数
//链栈 注:由于不知道有多少个元素,所以用链栈可以省空间
template<class DataType>
struct Node
{
DataType data;
Node<DataType> *next;
};
template<class DataType>
class LinkStack
{
public:
LinkStack(){top=NULL;num=0;}
~LinkStack(){};
void Push(DataType x);
DataType Pop();
DataType GetTop(){if(top!=NULL)return top->data;}
int Empty();
int num;
private:
Node<DataType> *top;
};
template<class DataType>//链栈入栈算法
void LinkStack<DataType>::Push(DataType x)
{
Node<DataType> *s;
s=new Node<DataType>;s->data=x;
s->next=top;top=s;
num++;
}
template<class DataType>//链栈出栈算法
DataType LinkStack<DataType>::Pop()
{
Node<DataType> *p;
p=new Node<DataType>;
if(top==NULL)throw"下溢";
DataType x=top->data;p=top;
top=top->next;
num--;
delete p;
return x;
}
template<class DataType>//判空操作
int LinkStack<DataType>::Empty()
{
if(top==NULL)
return 1;
else
return 0;
}
struct route
{
int x;
int y;
int direction;
};
const int Length=8;
const int Width=6;
int main()
{
/* 0 1 2 3 4 5 6 7 8 9
0 1 1 1 1 1 1 1 1 1 1
1 1 0 1 1 1 0 1 1 1 1
2 1 1 0 1 0 1 1 1 1 1
3 1 0 1 0 0 0 0 0 1 1
4 1 0 1 1 1 0 1 1 1 1
5 1 1 0 0 1 1 0 0 0 1
6 1 0 1 1 0 0 1 1 0 1
7 1 1 1 1 1 1 1 1 1 1
//检验迷宫如图所示,0代表能通行,1代表不能通行,起点为maze[1][1],终点为maze[Width][Length]*/
int i,j;
bool maze[Width+2][Length+2]=
{
{1,1,1,1,1,1,1,1,1,1},
{1,0,0,1,1,0,0,1,1,1},
{1,1,0,1,0,1,1,1,1,1},
{1,0,1,0,0,0,0,0,1,1},
{1,0,1,1,1,0,1,1,1,1},
{1,1,0,0,1,1,0,0,0,1},
{1,0,1,1,0,0,1,1,0,1},
{1,1,1,1,1,1,1,1,1,1}
};//初始化一个长度10,宽度8的二维数组,之所以要把长度宽度都加2,是因为需要一圈围墙来判断迷宫是否出界,并输入迷宫
//注:这里我定义横向为长,纵向为宽,注意,在定义二维数组一定要maze[宽+2][长+2],也就是先宽再长
cout<<"迷宫为:"<<endl;
for(i=0;i<Width+2;i++)
{
for(j=0;j<Length+2;j++)
cout<<maze[i][j]<<" ";
cout<<endl;
}//输出迷宫
bool flag[Width+2][Length+2]={0};//这个二维数组记录各个点是否到过,是为1,否为0
//以下是求取路径算法
LinkStack<route> Route;
int x=1,y=1;int direction=1;//当前的坐标,当前的方向
int next_x,next_y;//定义了下一步所走的点,如果x是横向的坐标,y是纵向的坐标,那么对应的点应该是maze[y][x]!!!这一点尤其注意,下面的同理,也需要这样表示
int out_able=1;//设置标志,如果栈为空了,就把其置零,表明无法走出迷宫,否则说明走出了while循环且到达了目的地
route NowNode;
NowNode.x=1;NowNode.y=1;NowNode.direction=3;//起点,对应的是(1,1,右)
flag[1][1]=1;//起点标记为来过
cout<<"老鼠探索迷宫的路线为:"<<endl;
while(next_x!=Length||next_y!=Width)//如果下一个坐标不是(8,6),这里要用到逻辑推理,A:x=8,B:y=6,出去的条件是AB,
//那么 ′(AB)=′A U ′B,即next_x!=Length||next_y!=Width
{
int move_flag=0;//定义是否移动了,0为否,1为是
for(direction=1;direction<=8;direction++)//按照方向direction从1到8循环
{
intend_move(x,y,next_x,next_y,direction);
if(!flag[next_y][next_x]&&!maze[next_y][next_x])//如果下个点没来过(flag为0),并且下个点可以通行(maze为0)
{
NowNode.x=x;NowNode.y=y;NowNode.direction=direction;//记录下来目前这个点的三个属性
Route.Push(NowNode);//把这个点压栈
cout<<"("<<x<<","<<y<<","<<direction<<")\t";
move(x,y,direction);//沿着这个方向移动一次
flag[y][x]=1;//新到的点标记为来过
move_flag=1;
break;
}
}
if(!move_flag)//如果没有移动过,即move_flag=0,说明此路不通,把路线倒回去一个,即出栈一个元素,再走
{
cout<<"("<<x<<","<<y<<","<<0<<")\t";//输出死角坐标(死角坐标是当前的x,y值,并未压入栈)
cout<<"("<<Route.GetTop().x<<","<<Route.GetTop().y<<","<<0<<")\t";//输出回退一个的坐标
x=Route.GetTop().x;y=Route.GetTop().y;direction=Route.GetTop().direction+1;//
Route.Pop();//要尤其注意这里的理解,走到死角之前的一个坐标方向是错的,所以这个点一定要出栈,但是在上一步中,我们已经保存了走到死角之前的那个坐标,所以就从死角前坐标的下一个方向再开始探索
}
if(Route.Empty())
{
cout<<"迷宫无法出去"<<endl;
out_able=0;
break;
}
}
if(out_able)
cout<<"终点(8,6,0)"<<endl;
int t=0,num=Route.num;
route *FinalRoute=new route[Route.num];
for(;t<num;t++)
FinalRoute[t]=Route.Pop();
cout<<"一条路径为:";
for(t=num-1;t>=0;t--)
cout<<"("<<FinalRoute[t].x<<","<<FinalRoute[t].y<<","<<FinalRoute[t].direction<<")"<<" ";
cout<<"终点(8,6,0)"<<endl;
cout<<endl;
return 0;
}
//这个函数是用来计算下一个的坐标
void intend_move(int x,int y,int &next_x,int &next_y,int direction)//1,2,3,4,5,6,7,8所对应的是8个方向,从上、右上、右……左上分别对应1,2,3,4,5,6,7,8
{
switch(direction)
{
case 1:
next_x=x;
next_y=y-1;
break;
case 2:
next_x=x+1;
next_y=y-1;
break;
case 3:
next_x=x+1;
next_y=y;
break;
case 4:
next_x=x+1;
next_y=y+1;
break;
case 5:
next_x=x;
next_y=y+1;
break;
case 6:
next_x=x-1;
next_y=y+1;
break;
case 7:
next_x=x-1;
next_y=y;
break;
case 8:
next_x=x-1;
next_y=y-1;
break;
}
}
void move(int &x,int &y,int &direction)
{
switch(direction)
{
case 1:
x=x;
y=y-1;
break;
case 2:
x=x+1;
y=y-1;
break;
case 3:
x=x+1;
y=y;
break;
case 4:
x=x+1;
y=y+1;
break;
case 5:
x=x;
y=y+1;
break;
case 6:
x=x-1;
y=y+1;
break;
case 7:
x=x-1;
y=y;
break;
case 8:
x=x-1;
y=y-1;
break;
}
direction=1;//移动后恢复坐标
}