问题描述
给定一个M*N的迷宫,给出起点终点坐标,每次只能向前后左右移动一格,求一条从起点到终点的路径(所求路径必须是简单路径,即路径不重复)
为了方便理解,我们先给定一个简单迷宫int mg[6][6]=
{
{1,1,1,1,1,1},
{1,0,0,0,1,1},
{1,0,1,0,0,1},
{1,0,0,0,1,1},
{1,1,0,0,0,1},
{1,1,1,1,1,1}
}; 规定0为可走方块,1为不可走方块(即墙壁)
设起点为(1,1),终点为(4,4)。(行列坐标皆从0开始)
简单说一下栈和队列,它们都是存放数据的容器,通常用于存放临时数据。栈实际上是一个后进先出表,队列是一个先进先出表。
下面先采用顺序栈来求解该问题:
准备阶段:
#include<stdio.h>
#include<stdbool.h> \\该头文件可将bool类型添加进c语言中
typedef struct
{
int i; \\方块行标
int j; \\方块列标
int di; \\方位变量
} box;
typedef struct
{
box data[25];\\存放路径坐标
int top; \\指向栈顶
} stktype; \\定义一个顺序栈类型
int mg[6][6]={{1,1,1,1,1,1},{1,0,0,0,1,1},{1,0,1,0,0,1},{1,0,0,0,1,1},{1,1,0,0,0,1},{1,1,1,1,1,1}};
函数定义:
bool mgpath(int xi,int yi,int xe,int ye) //xi,yi为起点,xe,ye为终点
{
int i,j,di,k,find;
stktype st; \\定义顺序栈
st.top=-1;
st.top++; \\元素进栈
st.data[st.top].i=xi;
st.data[st.top].j=yi;
st.data[st.top].di=-1;
mg[xi][yi]=-1; \\将其值置为-1,避免重复走
while(st.top>-1) \\栈不为空时开始循环
{
i=st.data[st.top].i;
j=st.data[st.top].j;
di=st.data[st.top].di;
if(i==xe && j==ye) \\如果取出的栈顶元素恰好为终点
{
printf("迷宫路径如下:\n");
for(k=0;k<=st.top;k++)
{
printf("\t(%d,%d)",st.data[k].i,st.data[k].j);
if((k+1)%5==0)
printf("\n");
}
printf("\n");
return true; //找到一条路径则返回true
}
//否则:
find = 0;
while(di<4 && find==0)//找下一个可走方块
{
di++;
switch(di) //为了方便,规定一个方块的上下左右方位分别是0,2,3,1
{
case 0:i=st.data[st.top].i-1;j=st.data[st.top].j;break;
case 1:i=st.data[st.top].i;j=st.data[st.top].j+1;break;
case 2:i=st.data[st.top].i+1;j=st.data[st.top].j;break;
case 3:i=st.data[st.top].i;j=st.data[st.top].j-1;break;
}
if(mg[i][j]==0) find=1;//找到了下一个可走方块i,j
}
if(find==1) //如果能找到下一个可走方块
{
st.data[st.top].di=di;//修改原栈顶元素di的值
st.top++;//下一个可走方块进栈
st.data[st.top].i=i;st.data[st.top].j=j;st.data[st.top].di=-1;
mg[i][j]=-1;
}
else//没找到可走方块
{
mg[st.data[st.top].i][st.data[st.top].j]=0;
st.top--;//退栈
}
}
}
函数实现:
void main()
{
if(!mgpath(1,1,4,4))
printf("该迷宫问题无解");
}
结果:
**迷宫路径如下:
(1,1) (1,2) (1,3) (2,3) (3,3)
(4,3) (4,4)
--------------------------------
Process exited after 2.621 seconds with return value 0
请按任意键继续. . .**
该算法实际上是先寻找当前方块相邻的可走方块(按0、1、2、3的顺序)并将其进栈,一直进行下去直到找到终点,就把栈中元素依次输出。如果一个方块周围皆不可走,则退栈,重新搜索。由这种结构思想限制,该算法只能找到一条路径,如果想找到多条路径,那么就要用到队列。
顺序队实现
#include<stdio.h>
#include<stdbool.h>
typedef struct
{
int i;
int j;
int pre; //本路径中上一方块在队列中的下标
} box;
typedef struct
{
box data[25];
int front,rear; //队头队尾指针
} qutype; //顺序队
int mg[6][6]={{1,1,1,1,1,1},{1,0,0,0,1,1},{1,0,1,0,0,1},{1,0,0,0,1,1},{1,1,0,0,0,1},{1,1,1,1,1,1}};
bool mgpath(int xi,int yi,int xe,int ye)
{
int a,i,j,di,find=0;
qutype qu;
qu.front=qu.rear=-1;
qu.rear++;
qu.data[qu.rear].i=xi;qu.data[qu.rear].j=yi; //xi,yi 进队
qu.data[qu.rear].pre=-1;
mg[xi][yi]=-1;
while(qu.front != qu.rear && !find)//队列非空开始循环
{
qu.front++;//出队
i=qu.data[qu.front].i;j=qu.data[qu.front].j;
if(i==xe && j==ye)//找到了出口,输出路径
{
find=1;
while(qu.front > 0) 输出队中所有元素
{
printf("\t(%d,%d)",qu.data[qu.front].i,qu.data[qu.front].j);
qu.front--;
printf("\n");
}
return true;
}
//否则把当前出队方块的每个相邻的可走方块进队
for(di=0;di<4;di++)
{
switch(di)
{
case 0:i=qu.data[qu.front].i-1;j=qu.data[qu.front].j;break;
case 1:i=qu.data[qu.front].i;j=qu.data[qu.front].j+1;break;
case 2:i=qu.data[qu.front].i+1;j=qu.data[qu.front].j;break;
case 3:i=qu.data[qu.front].i;j=qu.data[qu.front].j-1;break;
}
if(mg[i][j]==0)
{
qu.rear++;//将该相邻方块插入到队列中
qu.data[qu.rear].i=i;qu.data[qu.rear].j=j;qu.data[qu.rear].pre=qu.front;
mg[i][j]=-1;
}
}
}
return false;//未找到一条路径
}
void main()
{
if(!mgpath(1,1,4,4))
printf("该迷宫问题无解");
}
结果:
(4,4)
(4,3)
(4,2)
(3,3)
(2,4)
(3,2)
(2,3)
(3,1)
(1,3)
(2,1)
(1,2)
--------------------------------
Process exited after 0.5932 seconds with return value 0
请按任意键继续. . .
该解法整体结构与栈解法类似,但此解法将当前方块周围所有可走方块放入队列中,我们最后需从终点反向找到可走路径。易知该解法可找到多条路径,但处理过于麻烦,而且并不能找到所有路径。
那么我们想有没有一种方法可以简单的找到所有路径呢?
这时候,就要用到递归的思想了。
先给出实现代码:
#include<stdio.h>
typedef struct
{
int i; //当前方块行号
int j; //当前方块列号
} box;
typedef struct
{
box data[25];
int length;
} pathtype;
int mg[6][6]={{1,1,1,1,1,1},{1,0,0,0,1,1},{1,0,1,0,0,1},{1,0,0,0,1,1},{1,1,0,0,0,1},{1,1,1,1,1,1}};
int count = 0;
void mapath(int xi,int yi,int xe,int ye,pathtype path)
{
int di,k,i,j;
if (xi==xe && yi==ye) //递归出口
{
path.data[path.length].i = xi;
path.data[path.length].j = yi;
path.length++;
printf("迷宫路径如下%d:\n",++count);
for(k=0;k<path.length;k++)
{
printf("\t(%d,%d)",path.data[k].i,path.data[k].j);
if((k+1)%5==0)
printf("\n");
}
printf("\n");
}
else
{
if(mg[xi][yi]==0) //如果是可走方块
{
di=0;
while(di<4)
{
switch(di)
{
case 0:i=xi-1;j=yi;break;
case 1:i=xi;j=yi+1;break;
case 2:i=xi+1;j=yi;break;
case 3:i=xi;j=yi-1;break;
}
path.data[path.length].i=xi;
path.data[path.length].j=yi;
path.length++;
mg[xi][yi]=-1;
mapath(i,j,xe,ye,path); //如果找到可走方块则进行递归调用
path.length--; //找不到时将长度减一,回溯重新循环
mg[xi][yi] = 0;
di++;
}
}
}
}
main()
{
pathtype path;
path.length=0;
mapath(1,1,4,4,path);
}
结果:
迷宫路径如下1:
(1,1) (1,2) (1,3) (2,3) (3,3)
(4,3) (4,4)
迷宫路径如下2:
(1,1) (1,2) (1,3) (2,3) (3,3)
(3,2) (4,2) (4,3) (4,4)
迷宫路径如下3:
(1,1) (2,1) (3,1) (3,2) (3,3)
(4,3) (4,4)
迷宫路径如下4:
(1,1) (2,1) (3,1) (3,2) (4,2)
(4,3) (4,4)
--------------------------------
Process exited after 1.694 seconds with return value 4206656
请按任意键继续. . .
可以看出,该解法找出了所有的可走路径。
递归思想实际上是将一个大问题转化为小问题,这种方法明显减少了代码量,但却大大增加了思维量。
不过递归思想在计算机科学中非常重要,只要勤加练习,总可以熟练掌握,为自己的代码之路增加一个强有力的武器。
初入编程,如有什么错误或需要改进的地方还望各位大佬指点。