#define N 6
int maze[N][N] =
{
{0,0,0,0,0,0},
{0,0,1,1,1,0},
{0,0,1,0,1,0},
{0,0,1,1,1,0},
{0,0,1,0,1,1},
{0,0,1,0,0,0},
};
通过一个数字来创造一个6*6的迷宫,其中0代表墙,1代表能够走的路。
这里将数组通过画图软件画出来,这里红色的1代表迷宫的入口,绿色的1代表迷宫的出口。
这个数组所创建的迷宫是相对复杂的一种迷宫,首先这个迷宫是存在环的
这几个1,如果你的迷宫函数只是用简单的循环程序来走或者说通过普通的递归那你就很有可能程序崩掉,陷入一个死循环。
其次就是这个迷宫是存在两条路的,通过两条路可以出去,但是一个长一个短,应该通过什么算法才能找到那条短的路径,这是一个需要思考的问题。
最简单的方法,如果这个迷宫不带环的话,那完全可以通过递归来完成。
int CheckPath(int *a, Pos next, int n)
{
if (next._col < n && next._col >= 0
&& next._row >= 0 && next._row < n
&& (a[next._row*n + next._col] == 1))
return 1;
return 0;
}
首先,通过一个check函数来判断你想要走的位置是否能走,如果是下一个位置在迷宫数组内并且数值为1那就代表着可以走。
int MazePath(int *a, int n, Pos entry)
{
Pos cur = entry;
Pos next;
a[cur._row*n + cur._col] = 2;//将走过的地方的值全都改为2
if ((cur._col == 0) || (cur._col == n-1) || (cur._row == 0) || (cur._row == n-1))
return 1;
next = cur;
next._col++;//若下一个位置是当前位置的右边
if (CheckPath(a, next, n))//若下一个位置的值合法且为0(可以通),则跳到下一个位置,且压栈
//若不可以通那么就判断令一个方向的位置的值
{
cur = next;
MazePath(a, n, cur);
}
next = cur;
next._row++;
if (CheckPath(a, next, n))
{
cur = next;
MazePath(a, n, cur);
}
next = cur;
next._row--;
if (CheckPath(a, next, n))//同理
{
cur = next;
MazePath(a, n, cur);
}
next = cur;
next._col--;
if (CheckPath(a, next, n))//同理
{
cur = next;
MazePath(a, n, cur);
}
return 0;
}
这是最基础最简单的寻找出口的方法,如果当前的值是四周任意一边的一个位置那就代表着找到了出口,函数就返回1,否则的话就开始通过递归不断的往下走,这里我首先是让他往右如果不可以的话再走其他方向。这种方法走过的路程是不能回退的,也就是说你选择一条路之后如果发现这条路不通想退回去是不能实现的。所以这个方法有很大的缺陷。
如果想要将走过的函数回退其实有很多办法,当你所有都不能走的时候你可以走你之前走过的2的位置开始往回退,直到某个位置发现周围有1,那你就可以去走那个1去换一个路线来走,但是,其实想到回退大家很自然就能想到栈,你把你走过的路程每一个位置都压入到栈里边,如果不能走了那就从栈里边出一个数据,也就是回退到上一个位置,直到栈是空了,那就代表退到初始入口位置那就说明这个迷宫不存在通路。
while (Stackempty(S))//若栈不为空
{
if (cur._col != entry._col && cur._row != entry._row
&& (cur._col == n - 1 || cur._col == 0 || cur._row == n - 1 || cur._row == 0))//若该位置的行是最后一行,代表已走到出口位置
{
return true;
}
a[cur._row*n + cur._col] = 2;//将该位置置为2,代表已走过的路
next = cur;
next._col++;//若下一个位置是当前位置的右边
if (CheckPath(a, next, n))//若下一个位置的值合法且为0(可以通),则跳到下一个位置,且压栈
//若不可以通那么就判断令一个方向的位置的值
{
cur = next;
Stackpush(S,cur);
continue;
}
next = cur;
next._row++;
if (CheckPath(a, next, n))//同理
{
cur = next;
Stackpush(S, cur);
continue;
}
next = cur;
next._row--;
if (CheckPath(a, next, n))//同理
{
cur = next;
Stackpush(S, cur);
continue;
}
next = cur;
next._col--;
if (CheckPath(a, next, n))//同理
{
cur = next;
Stackpush(S, cur);
continue;
}
cur = Stacktop(S);//当四周都不可以通则回退,将当前位置置为栈顶元素的坐标位置
Stackpop();//顶元素弹出
}
这里只需要在上边函数中做一个小改动,首先需要通过一个循环来实现,循环的判断条件就是栈S不为空,如果不为空还能走就一直走,如果遇到了四周位置那就直接返回。
if (CheckPath(a, next, n))//同理
{
cur = next;
Stackpush(S, cur);
continue;
}
这里每到一个位置,之后就把这个位置的数据入栈,然后跳过本次循环,因为如果不跳过的话,你某一个位置好几个方向能走,他就会将这几个方向的位置全都存入栈中,跳出这次循环之后,会以刚刚入栈的这个当前位置为起点开始重新找他能够走的方向,
cur = Stacktop(S);//当四周都不可以通则回退,将当前位置置为栈顶元素的坐标位置
Stackpop();//顶元素弹出
如果某个位置判断了上下左右全都不能走,不符合条件那就说明这条路线是一条死路,那就需要将栈顶的元素获取出来也就是他的上一个位置,重新开始走,那是不是会存在你从这边退到上一个位置,然后又从上一个位置又走到当前位置的死循环呢?不会的,因为你每次走之前都会判断下个位置能不能走,如果你经过了某个位置,你就会把这个位置的数值变成2,下次再往这个方向走的时候判断条件就不成立。
通过栈来实现的话,当你走到出口的时候,还可以将栈的数据依次输出出来,那就能获得你所走过的路径。
栈解决了一个很大的问题,就是,你的当前路径失败的时候需要重新找到一个岔路口,从哪个岔路口的另一个方向开始走。但是现在还存在一个问题就是,当你的迷宫存在两条路的时候怎么办,如果你选择了一条长的路径就直接返回找到了路径,该怎么优化这个函数让他能找到你的最短路径。
这里有人运用了很巧妙的方法,假如我上图中画的迷宫,在找路径了找到了那个长的路径,之前我将每个每个走过的位置都变成了2,这里通过改进后的函数,是将你走过的每一个位置的数值都在前一个位置的基础之上加1
所以如果是找远的那个路径的话,走过之后的数组就会变成上图的样子,走的过程中,同样是需要将所走过的每个位置的数据压入栈之中,当你找到出口的时候,需要求一下当前栈中的数据个数,也就是你走过的位置的个数,就是你的路径的长度。这时候开始往回退你的栈,还是去找数值为1的位置,11附近没有,10附近没有继续退,到9的时候发现左边有一个1的元素,这时候把1位置变成比9大一个的数据就是变成了10。然后继续走发现周围没有1开始回退,到9发现还是没有1继续回退,一直退到3的时候,发现3的右边这个元素要比他周围其他元素要大,那就直接向右走
这是当前的情况,当开始从3开始走的时候,会往右走。这时候10位置的数据变成了4,然后继续走9位置的元素变成了5
这时候5周围有两个元素,分别是8和10,这和刚刚3的选择一样,选择周围所有方向中大的那个数据走。
最后迷宫会变成这个样子,这时并不知道迷宫的路径已经被你遍历完了,还是会开始回退,此时的栈中存的是红色的路径,开始回退,一直退到1 都没发现周围有能够再走的1元素,这时候说明所有路径已经被找完了,直接返回刚刚存下的栈中的数据的个数就是你最短路径的长度。