迷宫问题(1)–课程设计之简单实现(只找出一条通路即可)
https://blog.csdn.net/hansionz/article/details/80901089
因为迷宫问题需要用到栈,所以贴出栈的相关操作的链接
栈的基本操作
https://blog.csdn.net/hansionz/article/details/81636557
栈的相关面试题总结
https://blog.csdn.net/hansionz/article/details/81636768
迷宫地图的分类(大概有三种)
第一类:一个入口(entry),一个出口(exit),而且通路不带环,只有一条通路。
思路:首先,将入口坐标压入栈中,确定栈顶元素入口坐标为当前位置,并且将走过的坐标标记,以免走回头路。从当前位置开始探索上下左右四个方向,能通过则压栈继续切换当前位置前进,但是如果遇到死胡同(四个方向都不通),则回溯,既出栈一次,把栈顶坐标当做是当前位置,继续探索。探索过程中如果遇到终点,则直接结束;如果没有终点,则会一直回溯到起点,这时栈一定为空。此时,迷宫没有通路。
//给定一个终点,确定下一个结点是否可通
int CheckAccess_E(Pos cur)
{
//如果坐标没有越界,并且当前坐标为‘1’(代表路)才可以通过
//可以通过返回1,不可以通过返回0
if ((cur._row >= 0) && (cur._row < N) && (cur._col >= 0) &&
(cur._row < N) && (maze[cur._row][cur._col] == 1))
{
return 1;
}
else
{
return 0;
}
}
//一个入口,一个出口,求一条通路(返回1有通路,返回0没有通路)
int GetMazePath_E(Pos entry, Pos exit)
{
Stack s;
Pos next;
StackInit(&s);
//先将入口坐标压栈
StackPush(&s, entry);
//栈不为空说明还有坐标没有回溯完,继续回溯
while (StackEmpty(&s))
{
Pos cur = StackTop(&s);
//标记当前位置为已经走过的坐标
maze[cur._row][cur._col] = 2;
if ((cur._row == exit._row) && (cur._col == exit._col))
{
StackDestroy(&s);
return 1;
}
//右,切换坐标位置
next = cur;
next._col += 1;
if (CheckAccess_E(next))
{
StackPush(&s, next);
continue;
}
//左
next = cur;
next._col -= 1;
if (CheckAccess_E(next))
{
StackPush(&s, next);
continue;
}
//下
next = cur;
next._row += 1;
if (CheckAccess_E(next))
{
StackPush(&s, next);
continue;
}
//上
next = cur;
next._row -= 1;
if (CheckAccess_E(next))
{
StackPush(&s, next);
continue;
}
//当前坐标不通,则出栈回溯,把上一个结点当做当前位置继续探索
StackPop(&s);
}
return 0;
}
第二类:只给一个入口(entry),然后在一侧有多个出口(exit),必然有多条路径可通,怎么样才能求得一条最短的路径?
思路:在求解第一类迷宫的基础上,当遇到一个终点的时候,不要直接返回程序,而是继续探索回溯,找到下一个终点,继续回溯。然后,设置一个全局的变量来记录最短路径的长度,第一次到达终点,把路径长度赋值给全局变量,在以后的回溯中遇到终点则比较,如果小于全局变量的值,则更新。
//一个入口,多个出口,求一条最短通路
int pathsize=0;//全局变量,标记最短路径
void GetMazePath_M(Pos entry)
{
Stack s;
Pos next;
StackInit(&s);
//先将入口坐标压栈
StackPush(&s, entry);
//栈不为空说明还有坐标没有回溯完,继续回溯
while (StackEmpty(&s))
{
Pos cur = StackTop(&s);
//标记当前位置为已经走过的坐标
maze[cur._row][cur._col] = 2;
//出口都在列等5那列上,当前位置在出口上,不停止程序,继续回溯找下一条路径
if (cur._col == N - 1)
{
//如果当前路径长度小于全局变量,则更新;如果全局变量为0,则直接赋值
if ((StackSize(&s) < pathsize) || (pathsize == 0))
{
pathsize = StackSize(&s);
}
}
//右,切换坐标位置
next = cur;
next._col += 1;
if (CheckAccess_E(next))
{
StackPush(&s, next);
continue;
}
//左
next = cur;
next._col -= 1;
if (CheckAccess_E(next))
{
StackPush(&s, next);
continue;
}
//下
next = cur;
next._row += 1;
if (CheckAccess_E(next))
{
StackPush(&s, next);
continue;
}
//上
next = cur;
next._row -= 1;
if (CheckAccess_E(next))
{
StackPush(&s, next);
continue;
}
//当前坐标不通,则出栈回溯,把上一个结点当做当前位置继续探索
StackPop(&s);
}
}
上边的代码虽然很好的求出来最小路径,但是这个程序是有局限性的,它值适用于对迷宫路径中不带环的通路进行求解,如果迷宫中的路径是带环的,这道题的解法就是错误的,当然结果取决于切换下一个当前位置的方向,如果结果是正确的,也只能是运气好,但是思路是错误的。
第三类:通路间带环,怎么求解最短路径?
思路:换一种标记方式,从入口开始标记2,然后后边标记的值依次比前面标记的值要大于1。既然标记方式被改变了,那么回溯到前一个坐标时再去检查前一个坐标的下一个位置是否可通的方法也就被改变了。
//通路存在环,换一种标记方式,也要换一种检查是否可通的方式
int CheckAccess(Pos cur, Pos next)
{
//坐标不能越界
//必须为1(路)才能通过
//下一坐标的标记必须大于当前坐标标记加1才能通过
if ((next._row >= 0) && (next._row < N) && (next._col >= 0) &&(next._row < N)
&& ((maze[next._row][next._col] == 1)
||(maze[next._row][next._col] > maze[cur._row][cur._col] + 1)))
{
return 1;
}
else
{
return 0;
}
}
//给定一个终点,确定下一个结点是否可通
int CheckAccess_E(Pos cur)
{
//如果坐标没有越界,并且当前坐标为‘1’(代表路)才可以通过
//可以通过返回1,不可以通过返回0
if ((cur._row >= 0) && (cur._row < N) && (cur._col >= 0) &&
(cur._row < N) && (maze[cur._row][cur._col] == 1))
{
return 1;
}
else
{
return 0;
}
}
//通路之间带环
void GetMazePath_C(Pos entry)
{
Stack s;
Pos next;
StackInit(&s);
StackPush(&s, entry);
//从入口标记为2
maze[entry._row][entry._col] = 2;
while (StackEmpty(&s))
{
Pos cur = StackTop(&s);
//确定最短路径
if (cur._col == N-1)
{
if ((StackSize(&s) < stacksize) || (stacksize == 0))
{
stacksize = StackSize(&s);
}
}
//右,切换当前位置
next = cur;
next._col += 1;
if (CheckAccess(cur, next))
{
//下一个位置的标记值为上一个位置的标记值加1
maze[next._row][next._col] = maze[cur._row][cur._col] + 1;
StackPush(&s, next);
continue;
}
//左
next = cur;
next._col -= 1;
if (CheckAccess(cur, next))
{
maze[next._row][next._col] = maze[cur._row][cur._col] + 1;
StackPush(&s, next);
continue;
}
//下
next = cur;
next._row += 1;
if (CheckAccess(cur, next))
{
maze[next._row][next._col] = maze[cur._row][cur._col] + 1;
StackPush(&s, next);
continue;
}
//上
next = cur;
next._row -= 1;
if (CheckAccess(cur, next))
{
maze[next._row][next._col] = maze[cur._row][cur._col] + 1;
StackPush(&s, next);
continue;
}
//当前坐标不通,则回溯出栈
StackPop(&s);
}
}
为了测试方便,写一个打印迷宫的函数
//遍历二维数组打印迷宫
void PrintMaze(int maze[N][N])
{
for (size_t i = 0; i < N; i++)
{
for (size_t j = 0; j < N; j++)
{
printf("%d ", maze[i][j]);
}
printf("\n");
}
}
将迷宫打印出来,我们就可以清楚的看到迷宫的通路和我们做的标记。