问题描述:
在迷宫中求从入口到出口的一条简单路径。迷宫可用方块来表示, 每个方块或者是通道(用空白方块表示)或者是墙(用带阴影的方块表示)。 “迷宫求解”这个经典的问题,应用栈这种数据结构,自己设计一个方案, 并上机实现从入口到出口的一条简单路径。为了表示迷宫,用二维数组表示,如
int a[10][8]={{1,1,1,1,1,1,1,1},
{1,0,1,1,0,1,0,1},
{1,0,1,0,0,1,0,1},
{1,1,0,3,1,0,1,1},
{1,0,0,1,0,0,4,1},
{1,0,0,0,0,1,1,1},
{1,0,1,0,0,1,0,1},
{1,0,1,0,0,0,1,1},
{1,1,1,1,0,0,0,1},
{1,1,1,1,1,1,1,1}
};
3是入口,4是出口。
如下链接,可以查看更多详细信息。
http://jpkc.onlinesjtu.com/CourseShare/datastructure/TypicalArithmeticMain.aspx
参照网上一般思路:
在求解时,通常用的是“穷举求解”的方法,即从入口出发,顺某一方向向前试探,若能走通,则继续往前走;否则沿原路退回,换一个方向再继续试探,直至所有可能的通路都试探完为止。为了保证在任何位置上都能沿原路退回(称为回溯),需要用一个后进先出的栈来保存从入口到当前位置的路径。
我的理解:
用栈保存走过的路线,每个节点的数据又设为元素节点,包含横纵坐标信息,还有到达下一步的方向信息。如下所示
typedef struct{
int x;
int y;
int direction;
}node;
为了方便计算下一步的方向,用1,2,3,4表示左上右下,这样用变量自增就可得到下一步的方向。
node step[4]= {{0,-1,1},{-1,0,2},{0,1,3},{1,0,4}};//往左上右下的基坐标
具体思路:
其实这就是一个深度优先遍历的思想。
1.S走到A,即S入栈,并把如何到A的方式记录下来。当前位置设为A,下一步未知设为B。怎么到达B?
2.从A的左上右下四个方向开始试探。
if(找到一个方向可以走通且这个方向之前没有被试探过,就可以从这个方向进入B)
A入栈,并把这个方向存到A中,把B设为已访问。
else (如果找遍四个方向都行不通)
那么就把当前位置A回退到上一步S,并把A还原为未访问。由于S中保存了到A的方向,只要从到A的方向的下一个方向开始继续试探,得出怎么到A,再从这个A开始到B.进入2.知道达到出口程序结束循环。
具体代码如下:
#include "stdio.h"
#include "stdlib.h"
#define MAXSIZE 200
#define LINES 10 // 定义行数
#define COLS 8 // 定义列数
typedef struct{
int x;
int y;
int direction;
}node;
typedef struct{
node data[MAXSIZE];
int top;
}SeqStack,*PSeqStack;
PSeqStack Init_SeqStack()//初始化栈
{
PSeqStack S;
S = (PSeqStack)malloc(sizeof(SeqStack));
if(S)
{
S->top = -1;
}
return S;
}
int Empty_SeqStack(PSeqStack S)//判栈空
{
if(S->top == -1)
return 1;
else return 0;
}
int Push(PSeqStack S, int x, int y,int a)//入栈
{
if(S->top == MAXSIZE -1)
return 0;
else
{
S->top++;
S->data[S->top].direction = a;
S->data[S->top].y = y;
S->data[S->top].x = x;
return 1;
}
}
int Pop(PSeqStack S, int *x, int *y, int *a)//出栈
{
if(Empty_SeqStack(S))
return 0;
else
{
*x = S->data[S->top].x;
*y = S->data[S->top].y;
*a = S->data[S->top].direction;
S->top--;
return 1;
}
}
void Destroy_SeqStack (PSeqStack * SeqStackPoint)
{/*销毁顺序栈,入口参数:为要销毁的顺序栈指针地址*/
if (*SeqStackPoint) free (*SeqStackPoint) ;
*SeqStackPoint =NULL;
return ;
}
void visit(PSeqStack P)
{
int i,x,y,dir;
while(!Empty_SeqStack(P))
{
Pop(P,&x,&y,&dir);
printf("(%d,%d,%d)\n",x,y,dir);
}
}
int maze(int a[LINES][COLS])
{
int find,x,y,i,j,dir,xx,yy,xs,ys,xxx,yyy,dirr,xxxx,yyyy,dir0;
node step[4]= {{0,-1,1},{-1,0,2},{0,1,3},{1,0,4}};//往左上右下的基坐标
PSeqStack S;
S=Init_SeqStack();
for(i = 0; i < LINES; i++)//获取入口坐标
for(j = 0; j < COLS; j++)
{
if(a[i][j] == 3)
{
x = i;
y = j;
break;
}
}
Push(S,x,y,-1);//入口进栈 ,并将方向设为-1,表示不知道下一步的方向是多少,具体值需要下面确定,然后再放入栈中。
a[x][y] = -1;//访问入口 ,-1表示当前位置已经访问。1表示墙壁,0表示可行,3是入口,4是出口。
while(!Empty_SeqStack(S) )
{
/* Pop(S,&xx,&yy,&dir0); //为了得到当前栈顶的数据 。这种方式浪费时间 ,采用下面的读取栈顶元素的值合适 。
x = xx;
y = yy;
Push(S,xx,yy,dir0);*/
x = S->data[S->top].x;
y = S->data[S->top].y;
dir = S->data[S->top].direction;
printf("\nstack top element:(%d,%d,%d)",x,y,dir);
//走下一步B
find = 0;
//dir = -1;//从当前位置的左上右下开始。不能设置为-1,因为下面的回退一步,需要从上一个可行的方向开始自增。
printf("\ntest the direction: ");
while(!find && dir < 3)
{
dir++;
printf("%d ",dir);
switch(dir)
{
case 0:xs = x + step[dir].x; ys = y + step[dir].y;break;
case 1:xs = x + step[dir].x; ys = y + step[dir].y;break;
case 2:xs = x + step[dir].x; ys = y + step[dir].y;break;
case 3:xs = x + step[dir].x; ys = y + step[dir].y;break;
}
if((a[xs][ys] == 0) ||(a[xs][ys] == 4)) find = 1;
}
if(find == 1)//找到了下一个可走位置, A到B可走
{
Pop(S,&xxxx,&yyyy,&dirr);
Push(S,xxxx,yyyy,dir);
printf("changedir: %d ",dir);
Push(S,xs,ys,-1);//A入栈
if(a[xs][ys] == 0)
a[xs][ys] = -1;//设当前位置B访问过,-1表示访问过,
else //若该位置是出口,则算法结束
{
printf("\nthe reverse of simple rount:\n");
visit(S);
Destroy_SeqStack (&S);
return 1;
}
}
else //当前位置的下一步不可通过(当前位置为墙壁或者已经访问过)
{
a[x][y] = 0;//还原当前位置为未访问
//Pop(S,&xxx,&yyy,&dirr);//删去栈顶位置.但是需要变量存储栈顶元素,浪费空间。不妨直接退栈,采用下面的方式。
S->top--;//回退一步
}
}
if(Empty_SeqStack(S) )
{
printf("sorry");
Destroy_SeqStack (&S);
return 0;
}
}
void main()
{
int a[10][8]={{1,1,1,1,1,1,1,1},
{1,0,1,1,0,1,0,1},
{1,0,1,0,0,1,0,1},
{1,1,0,3,1,0,1,1},
{1,0,0,1,0,0,4,1},
{1,0,0,0,0,1,1,1},
{1,0,1,0,0,1,0,1},
{1,0,1,0,0,0,1,1},
{1,1,1,1,0,0,0,1},
{1,1,1,1,1,1,1,1}
};
maze(a);
}