最近复习一下数据结构,发现一个有趣的迷宫求解问题,当时就没去做,现在来玩玩这个问题。
迷宫问题的简单描述:给定一个迷宫,给定一个起点和终点,找到从起点到终点的一条可行路径。《数据结构(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; }