本文用栈解决迷宫问题,采用C语言实现,包括问题介绍、算法简介、求解过程、代码实现、结果展示。并附有完整代码。
本文完整代码:
https://pan.baidu.com/s/1r24R6mTlGHxUkEezWm4BjQ
提取码:2bbE
概括来说,迷宫问题就是在一个封闭空间里,事先不知道这个封闭空间的内部结构,经过个方向试探,从而找到一个出口。假设有个机器人放置在这个封闭空间的入口处:
由于迷宫构成是一个二维数组,我们可以采用坐标的形式 ( x , y ) (x,y) (x,y)来表示机器人的位置,为了方便代码实现,我将入口出的位置规定为(1,1)标蓝的位置为(1,1)如下图所示:
规定机器人探寻的方向为四个
dierct | incX | incY |
---|---|---|
0 | 0 | 1 |
1 | 1 | 0 |
2 | 0 | -1 |
3 | -1 | 0 |
我们用incX、incY分别表示x、y方向上的增量表示如果机器人往这个方向上移动其位置坐标将会发生如上的变化。可以看到上面这个方向是遵循右下左上的原则,我们就可以定义一个结构体数组分别保存incX、incY这两个变量,对其实例化后即可实现方向的增量表示。
//定义一个结构体表示方向的增量
typedef struct{
//x,y方向的增量
int incX,incY;
}Direction;
实例化:
Direction direct[4]= {
{
0,1},{
1,0},{
0,-1},{
-1,0}};
双层循环控制,内层循环控制机器人方向探寻过程,依次探寻四个方向,外层循环保证机器人进行回溯时退栈不为空。
if(maze[line][col]==0)
{
temp.x=x;
temp.y=y;
temp.di=di;
push(&S,temp.x ,temp.y ,temp.di);
x =line;
y =col;
maze[line][col]= -1;
while(!IsEmptyStack(&S))
{
pop(&S,&(temp.x),&(temp.y),&(temp.di));
x = temp.x;
y = temp.y;
di = temp.di +1;
while(di <4)
{
line = x+ direct[di].incX;
col = y + direct[di].incY;
if(maze[line][col]==0)
{
temp.x=x;
temp.y=y;
temp.di=di;
push(&S,temp.x ,temp.y ,temp.di);
x =line;
y =col;
maze[line][col]= -1;
if(x ==M && y == N)
{
//由于栈先入后出的特性这里重新定义一个栈目的就是能够正序输出其坐标
while(!IsEmptyStack(&S))
{
pop(&S,&(temp.x),&(temp.y),&(temp.di));
push(&H,temp.x ,temp.y ,temp.di);
}
while(!IsEmptyStack(&H))
{
pop(&H,&(temp.x),&(temp.y),&(temp.di));
printf("(%d,%d),direct: %d\n",temp.x,temp.y,temp.di);
}
return 1;
// int e1,e2,e3;
//GetTop(&S, &e1,&e2,&e3 );
// printf("%d",e1);
}
else di =0;
}
else
di ++;
}
}
while(!IsEmptyStack(&S))
{
pop(&S,&(temp.x),&(temp.y),&(temp.di));
push(&H,temp.x ,temp.y ,temp.di);
}
while(!IsEmptyStack(&H))
{
pop(&H,&(temp.x),&(temp.y),&(temp.di));
printf("(%d,%d),direct: %d\n",temp.x,temp.y,temp.di);
}
栈中需要保存位置坐标x、y以及从目前该位置移动至下一个位置的方向,保存这个方向的目的就在于走错路进行退栈。无需要四个方向重新遍历,在原有基础上查找为走过的方向即可。栈中保存的不再是一个数而应该是三个数,栈顶指针top更新时不可以简单的top++,理解也很简单这三个数分配的空间定义的一个结构体抽象数据类型用到的空间,具体是多少我们很难估计,基于此考虑利用双链表实现我们在定义保存当前位置坐标与方向这个结构体时定义同样类型的指针,分别指向他的前一个节点与后一个节点,这样在更新栈顶指针所指位置时,直接将next指针传给top就能顺利完成出栈与入栈。而对于出栈一样的操作,数据出栈后,只需要把parent指针的内容传给top。由此我们可以看出,栈的实现本质上变成了双向链表的操作。分别对应其入栈出栈。简单的画了一下栈内各结点的连接过程:
当我们考虑好结构体入栈出栈的具体实现方式,就要改变原来初始化的问题。进行栈的初始化,我采用的是提前动态申请部分空间,除了空间的申请更要构建好节点之间的关系,即能够正确找到next节点与parent节点。为了能够正确描述节点之间的关系选择采用循环申请的动态空间的方式。为了保证数据能够顺利入栈,栈顶指针需要指向下一个放入数据的空间,因此让栈顶指针等于栈底指针,初始化完成,得到一个空栈。
Status InitStack(SeqStack* stack)
{
//开辟空间
stack->base=stack->top = (EleType*)malloc( sizeof(Box));
int i =0;
for(i=0; i <50;i++)
{
stack->top->next = (EleType*)malloc( sizeof(Box));
stack->top->next->present = stack->top;
stack->top = stack->top->next;
}
stack ->top = stack ->base;
if (!stack->base)
{
exit(0);
}
stack->stackSize = STACK_INIT_SIZE;
return OK;
}