数据结构-----迷宫问题
作者:黑衣侠客
前言
最近学习数据结构中,需要完成老师布置的作业,所以,研究了下迷宫问题,看起来很难的迷宫问题,其实,解决方法有很多,下面我将为大家介绍,用栈是如何解决迷宫问题的。
思路
首先,我们应该在代码中布置迷宫的地图,在布置迷宫地图时,以二维数组来存储每个点的数值,二维数组的好处是可以用坐标进行表示,此时,我设的是1为障碍物(墙),0为通路,下面来看一下,我在代码中所写的迷宫地图。
使用二维数组来对迷宫地图进行编辑
之后,我们定义一个栈结构,栈的每一层用来存储走过的每个格子的横坐标,纵坐标,以及从这个格子到下一格子所对应的方向的数字(在这里,我定义的方向是:0-上,1-右,2-下,3-左),然后再定义一个int型的指针,用于从栈底一次遍历到栈顶,方便对每一层的数据进行操作。具体代码是这样的:
typedef struct
{
int i; //当前方块的行号
int j; //当前方块的列号
int di; //di是下一可走相邻方位的方位号---上下左右的标记
} Box;
typedef struct
{
Box data[MaxSize]; //是用来存储位置坐标的,当MaxSize<位置坐标的数目时,则程序出错,停止运行
int top; //栈顶指针
} StType; //定义栈类型
然后我们再来说一下,具体的操作方法:
1.定义了mgpath方法,用来执行具体的走迷宫的思想操作
int mgpath(int xi,int yi,int xe,int ye)
其中,xi为原点的横坐标,yi为原点的纵坐标,xe为终点的横坐标,ye为终点的纵坐标。
2.初始化之前定义的栈,将栈的指针top赋值-1,然后top自增为0,就是栈的第一层,也就是栈底,存放横纵坐标,以及方位值,然后将点所在位置标为-1,以免走重。
int i,j,k,di,find;
StType st; //定义栈st
st.top=-1; //初始化栈顶指针
st.top++; //初始方块进栈
st.data[st.top].i=xi;//栈底一号存入横坐标数值
st.data[st.top].j=yi;//栈底一号存入纵坐标数值
st.data[st.top].di=-1;//????????????????????????????????
mg[xi][yi]=-1;
3.这是最重要的一步,当栈不是空的时候就一直循环,直到找到终点为止,每次循环开始时,find都为0,而di为0~3,因此,满足条件时,进入内层循环,在内层循环的第一行有一个di++,原因是:每次循环开始时,di的值都为-1,di自增后,就满足了对横纵坐标操作的条件了,我按照上右下左的顺序,改变此时所在的位置坐标,(详解:当di=0时,若走不通,那么就再次进入循环,进行自增,依次类推…),然后,当找到下一个位置为0时(通路),find=1,然后top向上走,在创建一行空间,让每个走过的0位置,都变为-1,此外,需要注意是弹栈操作,当碰到死胡同的时候,那么就需要进行弹栈了,返回到上一步,并将该位置的值从-1变回到0(在进行图像操作时需要用到这一点),(这里不用担心会再次重复走到这个死胡同的位置,因为di++,直接在上一基础方位自增,而不是重新开始,所以不必担心这一点),至此,迷宫思想已全部完成,接下来就该做一些优化了。
while (st.top>-1) //栈不空时循环
{
i=st.data[st.top].i;
j=st.data[st.top].j;
di=st.data[st.top].di; //取栈顶方块
if (i==xe && j==ye) //找到了出口,输出路径
{
printf("迷宫路径如下:\n");
for (k=0; k<=st.top; k++)
{
printf("\t(%d,%d)",st.data[k].i,st.data[k].j);
if ((k+1)%5==0) //每输出5个元素,就换行
printf("\n");
}
printf("\n");
return 0; //找到路径之后,将所有路径打印出来,然后,程序结束
}
find=0;
while (di<4 && find==0) //找下一个可走方块
{
di++;
switch(di)
{
case 0://向上走
i=st.data[st.top].i-1;
j=st.data[st.top].j;
break;
case 1://向右走
i=st.data[st.top].i;
j=st.data[st.top].j+1;
break;
case 2://向下走
i=st.data[st.top].i+1;
j=st.data[st.top].j;
break;
case 3://向左走
i=st.data[st.top].i;
j=st.data[st.top].j-1;
break;
}
if (mg[i][j]==0) find=1; //mg[i][j]==0说明:该点可以走--------------(该点为此时所在点的下一预判点)
}
if (find==1) //找到了下一个可走方块
{
st.data[st.top].di=di; //修改原栈顶元素的di值
st.top++; //下一个可走方块进栈
st.data[st.top].i=i;
st.data[st.top].j=j;
st.data[st.top].di=-1; //再次初始化方向数值
mg[i][j]=-1; //避免重复走到该方块-----------------------//让每一个点都变成起点,因此,既满足递归的条件,也避免重复走到该位置
}
else //没有路径可走,则退栈-----------???????????为什么要退栈?不是刚刚只判断了一个方向吗?
{
mg[st.data[st.top].i][st.data[st.top].j]=0;//让该位置变为其他路径可走方块
st.top--; //将该方块退栈
}
4.我在主函数中,添加了一些优化操作,主要对输入原点以及图像做了简单处理。
while(1){
printf("请输入起点坐标:\n");
printf("(横坐标范围:0~9,纵坐标范围:0~9)\n");
scanf("%d %d",&x,&y);
if(mg[x][y]==1){
printf("输入坐标有误,该位置为墙,请重新输入:\n");
}else{
break;
}
}
图像处理:
printf("\n\n图像表示:\n");
for(t=0;t<10;t++){
printf("\t\t");
for(k=0;k<10;k++){
if(mg[t][k]==1){
printf("#");
}else if(mg[t][k]==0){
printf(" ");
}else{
printf("o");
}
}
if(k==10){
printf("\n");
}
}
printf("\n此时,o所代表的图标为迷宫行走路径!!!\n");
代码部分
#include <stdio.h>
#define MaxSize 100
#define M 8
#define N 8
int mg[M+2][N+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,0,1},
{1,0,1,1,1,0,0,0,0,1},
{1,0,0,0,1,0,0,0,0,1},
{1,0,1,0,0,0,1,0,0,1},
{1,0,1,1,1,0,1,1,0,1},
{1,1,0,0,0,0,0,0,0,1},
{1,1,1,1,1,1,1,1,1,1}
};//构造地图
typedef struct
{
int i; //当前方块的行号
int j; //当前方块的列号
int di; //di是下一可走相邻方位的方位号---上下左右的标记
} Box;
typedef struct
{
Box data[MaxSize]; //是用来存储位置坐标的,当MaxSize<位置坐标的数目时,则程序出错,停止运行
int top; //栈顶指针
} StType; //定义栈类型
//该栈中:每一个位置用于存储Box,该栈对应了一个int型的top作为指针,依次移动,方便对栈中每一个位置进行操作,而Box中,存储了该迷宫位置的横坐标,纵坐标,和从上一位置移动到该位置所对应的方向数值
int mgpath(int xi,int yi,int xe,int ye) //求解路径为:(xi,yi)->(xe,ye)
//初位置横坐标,初位置纵坐标,终点横坐标,终点纵坐标
{
int i,j,k,di,find;
StType st; //定义栈st
st.top=-1; //初始化栈顶指针
st.top++; //初始方块进栈
st.data[st.top].i=xi;//栈底一号存入横坐标数值
st.data[st.top].j=yi;//栈底一号存入纵坐标数值
st.data[st.top].di=-1;//????????????????????????????????
mg[xi][yi]=-1;
while (st.top>-1) //栈不空时循环
{
i=st.data[st.top].i;
j=st.data[st.top].j;
di=st.data[st.top].di; //取栈顶方块
if (i==xe && j==ye) //找到了出口,输出路径
{
printf("迷宫路径如下:\n");
for (k=0; k<=st.top; k++)
{
printf("\t(%d,%d)",st.data[k].i,st.data[k].j);
if ((k+1)%5==0) //每输出5个元素,就换行
printf("\n");
}
printf("\n");
return 0; //找到路径之后,将所有路径打印出来,然后,程序结束
}
find=0;
while (di<4 && find==0) //找下一个可走方块
{
di++;
switch(di)
{
case 0://向上走
i=st.data[st.top].i-1;
j=st.data[st.top].j;
break;
case 1://向右走
i=st.data[st.top].i;
j=st.data[st.top].j+1;
break;
case 2://向下走
i=st.data[st.top].i+1;
j=st.data[st.top].j;
break;
case 3://向左走
i=st.data[st.top].i;
j=st.data[st.top].j-1;
break;
}
if (mg[i][j]==0) find=1; //mg[i][j]==0说明:该点可以走--------------(该点为此时所在点的下一预判点)
}
if (find==1) //找到了下一个可走方块
{
st.data[st.top].di=di; //修改原栈顶元素的di值
st.top++; //下一个可走方块进栈
st.data[st.top].i=i;
st.data[st.top].j=j;
st.data[st.top].di=-1; //再次初始化方向数值
mg[i][j]=-1; //避免重复走到该方块-----------------------//让每一个点都变成起点,因此,既满足递归的条件,也避免重复走到该位置
}
else //没有路径可走,则退栈-----------???????????为什么要退栈?不是刚刚只判断了一个方向吗?
{
mg[st.data[st.top].i][st.data[st.top].j]=0;//让该位置变为其他路径可走方块
st.top--; //将该方块退栈
}
}
return(0); //表示没有可走路径,返回0
}
int main()
{
int x,y,k,t;
printf("终点坐标为:(8,8)\n");
while(1){
printf("请输入起点坐标:\n");
printf("(横坐标范围:0~9,纵坐标范围:0~9)\n");
scanf("%d %d",&x,&y);
if(mg[x][y]==1){
printf("输入坐标有误,该位置为墙,请重新输入:\n");
}else{
break;
}
}
mgpath(x,y,M,N);
printf("\n\n图像表示:\n");
for(t=0;t<10;t++){
printf("\t\t");
for(k=0;k<10;k++){
if(mg[t][k]==1){
printf("#");
}else if(mg[t][k]==0){
printf(" ");
}else{
printf("o");
}
}
if(k==10){
printf("\n");
}
}
printf("\n此时,o所代表的图标为迷宫行走路径!!!\n");
return 0;
}