迷宫问题的总体思路是,从迷宫的入口出发,沿着某一个方向向前试探,若能够行得通,则继续往前走,否则原来返回,再换另一个方向继续试探,直到所有可能的通路都被试探过,为了保证在任何一个位置都能够原来返回,需要设置一个堆栈结构来保存从入口到当前位置的路径。因为可以需要保证能够返回所在节点,所以需要回溯法能够保证回到走过的节点。
本文的解法 参考 《数据结构 C》 李春葆 - 非迭代法, 迭代法 原创。迷宫用二维数组 A 表示: 最外围的1 表示墙, 内部1表示墙, 0 表示通路。使用非迭代方法, C程序如下
int findPath(int in_x, int in_y, int out_x, int out_y)
{
int A[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,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
// 栈用于存储走过的节点
struct
{
int x;
int y;
int next; // how many ways to go or left to go in this node
}st[MAXSTACKSIZE];
int top = -1;
// 将入口添加到栈中
top++;
st[top].x = in_x;
st[top].y = in_y;
st[top].next = -1;
A[in_x][in_y] = -1;
int cnt_i, cnt_j, cnt_next, find_way;
while(top > -1)
{
cnt_i = st[top].x; cnt_j = st[top].y;
cnt_next = st[top].next; // 取得当前节点 同时查看当前节点还有几个下一个节点可以走
//printf("at [%d %d]\n", cnt_i, cnt_j);
// find the path
if(cnt_i == out_x && cnt_j == out_y)
{
printf("\n\nFind the path:");
for(int k = 0; k <= top; k++)
{
printf("\t[%d %d]", st[k].x, st[k].y);
if(k % 5 == 0)
{
printf("\n");
}
}
printf("\n\n");
} // end of find path
// find next way to go
find_way = 0;
while(cnt_next < 4 && find_way == 0)
{
cnt_next++;
switch(cnt_next)
{
case 0: cnt_i = st[top].x - 1; cnt_j = st[top].y; break;
case 1: cnt_i = st[top].x; cnt_j = st[top].y + 1; break;
case 2: cnt_i = st[top].x + 1; cnt_j = st[top].y; break;
case 3: cnt_i = st[top].x; cnt_j = st[top].y - 1; break;
}
if(0 == A[cnt_i][cnt_j]) find_way = 1; // 有路可以走
}
if(find_way == 1)
{ // find way and stack it
st[top].next = cnt_next;
top++;
st[top].x = cnt_i;
st[top].y = cnt_j;
st[top].next = -1;
A[cnt_i][cnt_j] = -1;
}else
{ // do not find way in this node;
// pop out the node
A[st[top].x][st[top].y] = 0;
top --;
}
}
return 0; // no way to go and return 0;
}
使用迭代方法
int top_iter;
int A_iter[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,0,1},
{1,1,1,1,1,1,1,1,1,1}
};
int find_way_iter = 0;
int findPathIterative(int in_x, int in_y, int out_x, int out_y)
{
top_iter = -1;
top_iter++;
st_iter[top_iter].x = in_x;
st_iter[top_iter].y = in_y;
A_iter[in_x][in_y] = -1;
_findPathIterative(in_x, in_y, out_x, out_y);
if(find_way_iter == 0)
{
printf("No way to go");
}
return 0;
}
void _findPathIterative(int in_x, int in_y, int out_x, int out_y)
{
// find the path
if(in_x == out_x && in_y == out_y)
{
printf("\n\nFind the path:");
find_way_iter = 1;
for(int k = 0; k <= top_iter; k++)
{
printf("\t[%d %d]", st_iter[k].x, st_iter[k].y);
if(k % 5 == 0)
{
printf("\n");
}
}
printf("\n\n");
} // end of find path
// up node to iterate
if(A_iter[in_x - 1][in_y] == 0)
{
PushtoStack(in_x - 1, in_y); // push the node into stack and add covered tag
_findPathIterative(in_x - 1, in_y, out_x, out_y);
PopFromStack(); // pop out the node and remove the tag
}
// right node to iterate
if(A_iter[in_x][in_y+1] == 0)
{
PushtoStack(in_x, in_y + 1);
_findPathIterative(in_x, in_y + 1, out_x, out_y);
PopFromStack();
}
// bottom node to iterate
if(A_iter[in_x + 1][in_y] == 0)
{
PushtoStack(in_x + 1, in_y);
_findPathIterative(in_x + 1, in_y, out_x, out_y);
PopFromStack();
}
// left node to iterate
if(A_iter[in_x][in_y - 1] == 0)
{
PushtoStack(in_x, in_y - 1);
_findPathIterative(in_x, in_y - 1, out_x, out_y);
PopFromStack();
}
}
void PopFromStack()
{
A_iter[st_iter[top_iter].x][st_iter[top_iter].y] = 0; // out of this node
top_iter --;
}
void PushtoStack(int x, int y)
{
top_iter++;
st_iter[top_iter].x = x;
st_iter[top_iter].y = y;
A_iter[x][y] = -1; // covered this node
}
上述迭代方法 没有办法找到最优解(最短路径)。
只能找到所有路径。
比较上述两个方法
迭代方法 用时303ms; 非迭代方法587ms
迭代方法明显优于非迭代方法,并且易于理解。