前言
最近复习一下数据结构,发现一个有趣的迷宫求解问题,当时就没去做,现在来玩玩这个问题。
迷宫问题的简单描述:给定一个迷宫,给定一个起点和终点,找到从起点到终点的一条可行路径。《数据结构(C语言版)》书中的图如下:
解决思路:
1.把上面的迷宫转换为一个二维矩阵,用0表示空白,1表示障碍。
2.从起点开始,用逐步探测的方法寻找路径
从当前的位置开始探测,按顺时针探测四个方向,
如果该方向不可行,则标记之。循环探测下一个方向,直到探测到一个可行的方向,否则进行回退操作,把该位置标记为障碍,即此路不通。
找到可行的方向,把当前位置做个标记,留下脚印,防止在以后的探测中又回到原地,进入死循环。接着挪到这个方向的下一步。
循环该算法,直到找到终点。
上面的算法中,涉及到回退的操作,可以用栈来帮助解决。下面的程序实现中写了个简单的Stack实现,支持PUSH和POP操作。
这里比较困难的也许就是如何定义位置的信息结构了,从上面的分析,位置position有以下一些基本信息:1.位置的坐标(i,j);2.处于这个位置时,前进的方向;3.各个方向的可行状态,例如坐标(1,1)北、东、南、西的方向状态为(1,0,0,1),1表示不可行,1表示可行。这样就可以定义出位置的信息结构体了:
typedef struct{
int i;
int j;
int directions[4];//north,east,south,west,0 means can go throuth,1 means can not go through
int cur_dir; //current direction
}pos;
这样,每当挪动一步,就把该位置信息入栈。当路径不通是可以进行出栈,进行回退探测。
挪动时有个细节,例如从position1往南挪一步到position2,那么position2的北方向应该标记为不可行,为什么呢?很简单,要是可以往北的话,那就相当于回退一步了。
回退操作时也有个细节,例如从position3往东挪一步到position4,在以后的探测中发现position4继续走下去的路是走不通的,那么需要进行回退,退到position3,此时,position3的往东方向应该标记为不可行,然后再探测其它方向。
我们需要的几个主要的函数:
1.给定一个坐标,返回该坐标的可行状态,可行还是不可行,还是到终点了 pos_status
2.在一个位置上,找到下一个可行的方向。 pos_get_dir
3.在一个位置上,往可行的方向挪动下一步,没找到就进行回退探测。 pos_go
好了,预览下效果再看源代码,其中空白表示可行区域,1表示障碍,2表示终点。找到路径后用箭头把整个路径的前进方向整出来(很帅吧):
源码实现
#include <stdio.h>
#include <stdlib.h>
#define STACK_SIZE 1000
#define STACK_INCREMENT 100
#define STACK_PUSH(stack_p,element) do{\
if( (stack_p)->count >= (stack_p)->size){\
(stack_p)->base = (stack_element *)realloc( (stack_p)->base,( (stack_p)->size + STACK_INCREMENT ) * sizeof(stack_element) );\
}\
*(stack_p)->top = (stack_element)(element);\
(stack_p)->top++;\
(stack_p)->count++;\
}while(0);
#define STACK_POP(stack_p,element_p) do{\
if( (stack_p)->top == (stack_p)->base){\
element_p = NULL;\
}else{\
(stack_p)->top--;\
*element_p = *(stack_p)->top;\
}\
}while(0);
/**position struct*/
typedef struct{
int i;
int j;
int directions[4];//north,east,south,west,0 means can go throuth,1 means can not go through
int cur_dir; //current direction
}pos;
typedef pos stack_element;
typedef struct{
stack_element * base;
stack_element * top;
int size;
int count;
}stack;
/**Initial the stack */
int stack_init(stack *s){
s->base = (stack_element *)malloc(STACK_SIZE * sizeof(stack_element));
if(!s->base){
return 1;
}
s->top = s->base;
s->size = STACK_SIZE;
s->count = 0;
return 0;
}
/** Get the status of maze position */
int pos_status(pos *p,int dir,int maze[][10]){
int status;
switch(dir){
case 0:
status = maze[ p->i -1 ][ p->j ];
break;
case 1:
status = maze[ p->i ][ p->j + 1 ];
break;
case 2:
status = maze[ p->i +1 ][ p->j ];
break;
case 3:
status = maze[ p->i ][ p->j - 1 ];
break;
}
return status;
}
/** Get the next available direction of current position ,return -1 if not available */
int pos_get_dir(pos *p,int maze[][10]){
int status,tmp_dir;
status = pos_status(p,p->cur_dir,maze);
if(status != 1){
return p->cur_dir;
}
p->directions[p->cur_dir] = 1;
tmp_dir = p->cur_dir;
do{
tmp_dir = (tmp_dir+1)%4;
if(p->directions[tmp_dir] == 1) continue;
status = pos_status(p,tmp_dir,maze);
if(status == 1){
p->directions[tmp_dir] = 1;
}else{
p->cur_dir = tmp_dir;
return p->cur_dir;
}
}while(p->cur_dir != tmp_dir);
return -1;//no directions available
}
/** Go to the next available position and return 0, return -1 if break down */
int pos_go(pos *p,int maze[][10],stack *sp){
/* printf("%d,%d\n",p->i,p->j);*/
int dir,i;
dir = pos_get_dir(p,maze);
if(dir != -1){
STACK_PUSH(sp,*p);
maze[p->i][p->j] = 1;//mark and leave foot print,avoid recursive going back
p->directions[3-dir] = 1;//do not go back
switch(dir){
case 0:
p->i--; break;
case 1:
p->j++; break;
case 2:
p->i++; break;
case 3:
p->j--; break;
}
for(i=0;i<4;i++){//reset the directions except the current going direction
if(i != dir){
p->directions[i] = 0;
}
}
}else{
maze[p->i][p->j] = 1;//can no go through
STACK_POP(sp,p);
if(NULL == p){
return -1;
}else{
p->directions[p->cur_dir] = 1;
}
}
return 0;
}
int main(){
stack s;
stack_init(&s);
int maze[10][10] = {
{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,2,1},
{1,1,1,1,1,1,1,1,1,1},
};
/**printf the original maze*/
int i,j;
for(i=0;i<10;i++){
for(j=0;j<10;j++){
if(maze[i][j] == 0){
printf(" ");
}else{
printf("%d",maze[i][j]);
}
}
printf("\n");
}
pos goner = {1,1,{0,0,0,0},0};
while(1){
if(maze[goner.i][goner.j] == 2){
STACK_PUSH(&s,goner);
break;
}
if(-1 == pos_go(&goner,maze,&s)){
printf("no way!\n");
break;
}
}
/** printf path */
char * directions[4] = {"↑","→","↓","←"};
pos *gp;
gp = &goner;
while(1){
STACK_POP((&s),gp);
if(NULL != gp){
maze[gp->i][gp->j] = gp->cur_dir + 4;
}else{
break;
}
}
for(i=0;i<10;i++){
for(j=0;j<10;j++){
if(maze[i][j] == 0){
printf(" ");
}else if(maze[i][j] == 1){
printf("1");
}else{
printf("%s",directions[ maze[i][j] - 4 ]);
}
}
printf("\n");
}
return 0;
}