求迷宫中从入口到出口的所有路径是一个经典的程序设计问题。由于计算机解迷宫时,通常用的是穷举求解的方法,即从入口出发,顺某一方向向前探索,若能走通,则继续往前走,否则沿原路退回,换一个方向再继续探索,直至所有可能的通路都探索到为止。为了保证在任何位置上都能沿原路退回,用栈来保存从入口到当前位置的路径就是自然的事情了。
输入:入口位置、出口位置
输出:迷宫路径、具体路径。
运行结果:
算法思路:
令curPos指向入口,curStep为1,重复
while(TRUE)
{
若当前位置通,则将其纳入路径(栈),是出口停,非出口重置curPos为右邻,开始下次循环。若当前位置不同,看上一步,若其四周均已访问过,则将其从路径中删除,如此重复直到找到一个曾经访问之位置。其四个方向至少有一个未曾访问过,此时重置curPos为此未访问邻块,开始下次循环,若栈空仍然未找到则退出。
}
首先是辅助宏的定义:
//墙及通路及前进方向符号定义
#define WALL 0 //代表当前格子是墙
#define PATH 1 //代表是通路且未走过
#define RIGHT -1 //代表是通路且从其向右走
#define DOWN -2 //代表是通路且从其向下走
#define LEFT -3 //代表是通路且从其向左走
#define UP -4 //代表是通路且从其向上走
#define BACK -5 //代表是通路且从其后退一步
#define DESTINATION -6//代表是通路且是目标位置
#define EXIT -7
typedef int MazeType[10][10];//最外初始化成墙
typedef int Status;
typedef int ElemType;//迷宫中数组的元素类型
栈中元素类型定义:
typedef struct
{
int x,y;
} PosType;
typedef struct
{
int ord;//通道块在路径上的序号
PosType seat;//通道块在迷宫的位置
int di; //从此通道块到下一通道块的方向
} SElemType;
在迷宫中留下足迹。
void FootPrint(MazeType &maze,PosType curpos,Status s)
{
//留下足迹
maze[curpos.x][curpos.y]=s;
}
返回下一个位置.
PosType Nextpos(PosType curpos,Status s)
{
//返回下一个位置
switch(s)
{
case RIGHT:
curpos.y++;
break;
case DOWN:
curpos.x++;
break;
case LEFT:
curpos.y--;
break;
case UP:
curpos.x--;
break;
}
return curpos;
}
生成迷宫,0表示通PATH,1表示不通WALL.
//迷宫求解的具体算法
Status MakeMaze(MazeType &maze)
{
//生成迷宫,0表示通PATH,1表示不通WALL
int x,y;
srand(time(NULL));
for(y=0; y<=9; y++)
{
maze[0][y]=WALL;
maze[9][y]=WALL;
}
for(x=1; x<=8; x++)
{
maze[x][0]=WALL;
maze[x][9]=WALL;
}
for(x=1; x<=8; x++)
for(y=1; y<=8; y++)
maze[x][y]=rand()%2;
return OK;
}
输出迷宫.
void PrintMaze(MazeType maze)
{
//输出迷宫
int x,y;
for(x=0; x<=9; x++)
{
for(y=0; y<=9; y++)
{
switch(maze[x][y])
{
case WALL:
printf("■");
break;
case PATH:
printf(" ");
break;
case RIGHT:
printf("→");
break;
case DOWN:
printf("↓");
break;
case LEFT:
printf("←");
break;
case UP:
printf("↑");
break;
case BACK:
printf("@ ");
break;
case DESTINATION:
printf("◎");
break;
default:
printf("error");
}
}
printf("\n");
}
}
输出具体路径.
Status PrintSElem(SElemType e)
{
//输出坐标
printf("(%d,%d)\n",e.seat.x,e.seat.y);
return OK;
}
void StackTraverse(SqStack S,Status (*PrintSElem)(SElemType))
{
//输出具体路径
int len=S.top-S.base;
for(int i=1; i<=len; i++)
{
printf("step:%d to ",i);
PrintSElem(S.base[i-1]);
}
}
算法实现:
求迷宫中start到end的通路,有通路返回TRUE,没通路返回FALSE,栈S返回路径。
Status PassMaze(MazeType &maze,PosType start,PosType end,SqStack &S)
{
//求迷宫中start到end的通路,有通路返回TRUE,没通路返回FALSE,栈S返回路径
PosType curpos=start;//当前位置为开始位置
int curstep=1;//探索第一步
SElemType e;
if(maze[curpos.x][curpos.y]!=PATH)//入口不通则停
return FALSE;
while(1) //从当前位置处理,不断找cospos重复,直到栈空或者找到出口
{
if(maze[curpos.x][curpos.y]==PATH) //当前位置可通
{
e.di=RIGHT;//右邻马上被访问
e.seat=curpos;
e.ord=curstep;
Push(S,e);//加入路径
if(curpos.x==end.x&&curpos.y==end.y) //到达终点
{
FootPrint(maze,curpos,DESTINATION);//留下足迹
return OK;
}
else
{
FootPrint(maze,curpos,RIGHT);
curpos=Nextpos(curpos,RIGHT);//下一位置是当前位置的东临
}
}
else //当前位置不通
{
if(StackEmpty(S))
return FALSE;
GetTop(S,e);//取最近到过的位置查看信息
while(e.di==-4) //四周都访问过
{
Pop(S,e);
curstep--;
FootPrint(maze,e.seat,BACK);//后退一步 留下不能通过的标记
GetTop(S,e);
}
if(e.di>-4) //栈顶位置有其他方向可以选择
{
Pop(S,e);
e.di--; //换一个方向
Push(S,e);//修改栈顶位置信息
FootPrint(maze,e.seat,e.di);
curpos=Nextpos(e.seat,e.di);//设定当前位置是新方向上的相邻块
}
}
}
return OK;
}