方法一:使用栈做回溯控制
在迷宫内的道路只能向上下左右四个方向行走,每次只能走一小步。笔者默认的行走优先级是上、下、左和右(下方左图)。对角的方向不能行走(下方左图)。

如果遇到墙壁阻挡,就需要尝试剩下几个方向是否有可行的路,继续同样的走法直到找出走出迷宫的路。迷宫的图形用二维数组表示,数组0表示是可以走的路,1代表墙壁。见下图

其中,X表示目前的位置,出口在做上角,第一步尝试往上走,此时的位置为

上图中数字2表示已经走过的路,接着尝试往上走,发现是墙壁,下方是已经走过的路,此时只有左边的路可以走,所以往左走,

因为我们的假定优先级为先走上,且发现上方有路,所以往上走,接着上方仍然有路,所以接着往上走,此时在右方有路,然后往右走,如下图

现在惨了,已经没有路可以走了,所以使用回溯的概念,退一步看看有没有其它的路。从上面的迷宫可以知道我们需要连退三步,才会有路可以走

数字3表示退回的路,现在左方有路可以走,接着上方、右方和下方都是墙壁或者走过的路,所以继续向左走,

现在一步一步地往上走,就可以找到走出迷宫的路。
这种查询的方法称为“尝试错误”,而一步一步地退回的技巧称为回溯。在写程序时,我们用栈来处理回溯的操作。
下面来看看如何使用栈走迷宫。迷宫问题的迷宫是一个二维数组,如下所示。

如前描述,数字1代表墙壁,数字0表示可以走。为了简化数组边界的检查,我们在四周都加上了墙壁,所以在编程时不会超出数组的界限,而真正的迷宫只有中间双框的部分。
Python2.7编写
#使用栈走迷宫(回溯)
def find_path(maze):
x, y = 5, 8 #入口
path = []
path.append((x,y))
while x != 1 or y != 1: #是否是迷宫的出口
maze[x][y] = 2 #已走过的路
if maze[x-1][y] <= 0: #往上方走
x = x - 1
path.append((x,y)) #存入路径
elif maze[x+1][y] <= 0: #往下方走
x = x + 1
path.append((x,y)) #存入路径
elif maze[x][y-1] <= 0: #往左方走
y = y - 1
path.append((x,y)) #存入路径
elif maze[x][y+1] <= 0: #往右方走
y = y + 1
path.append((x,y)) #存入路径
else: #没有路可走:回溯
maze[x][y] = 3 #表示回溯的路
x,y = path.pop() #退回一步
maze[x][y] = 2 #最后把出口标示为已走过
printMaze(maze) #调用打印迷宫的函数
#打印迷宫
def printMaze(maze):
for i in range(1,6):
for j in range(1,9):
print "%d" %maze[i][j],
print "\n"
#主程序:用回溯的方法在数组迷宫找出口
#数字0: 表示是可走的路
#数字1:表示墙壁,不可以走的路
#数字2:表示走过的路
#数字3:表示回溯的路
if __name__ == "__main__":
#示例迷宫
maze = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 1, 0, 1],
[1, 0, 1, 0, 1, 1, 1, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 0, 1, 1],
[1, 0, 0, 0, 1, 1, 1, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
find_path(maze)
输出:
2 1 3 1 3 3 3 3
2 1 3 1 3 1 1 3
2 1 3 1 1 1 3 3
2 1 2 2 2 2 2 1
2 2 2 1 1 1 2 2
方法二:使用递归找迷宫出口
走迷宫的规则:目前坐标是(i, j),下一步可以行走的路是往上方,下方,左方和右方,如下图

我们假定优先级为上、下、左和右,也就是说,第一优先是往上走,如果上方可以走就往上走,不行的话尝试第二优先的下方,如果不行再尝试第三优先和第四优先。
我们从入口开始以上述四个方向寻找可以走动的下一个位置,如果找到可以走的位置,就将其值设置为2以表示走过。如果四个方向都不能走,则表示此位置是一条死路,所以必须将其重置为0,继续不断尝试错误找到可行的路径,这就是回溯控制。
Python2.7编写
#使用递归走迷宫
def find_path(x, y):
if x == 1 and y == 1: #是否是迷宫出口
maze[x][y] = 2 #记录最后走过的路径
return 1
else:
if maze[x][y] == 0: #是不是可以走
maze[x][y] = 2 #记录已经走过的路径
#调用递归函数:分别是往上、往下、往左和往右
if find_path(x - 1, y) + find_path(x + 1, y) + find_path(x, y - 1) + find_path(x, y + 1) > 0:
return 1
else:
maze[x][y] = 0 #此路不通,取消记号
return 0
else:
return 0
#打印迷宫
def printMaze(maze):
for i in range(1,6):
for j in range(1,9):
print "%d" %maze[i][j],
print "\n"
#主程序:用回溯的方法在数组迷宫找出口
#数字0: 表示是可走的路
#数字1:表示墙壁,不可以走的路
#数字2:表示走过的路
#数字3:表示回溯的路
if __name__ == "__main__":
#示例迷宫
maze = [[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[1, 0, 1, 0, 1, 0, 0, 0, 0, 1],
[1, 0, 1, 0, 1, 0, 1, 1, 0, 1],
[1, 0, 1, 0, 1, 1, 1, 0, 0, 1],
[1, 0, 1, 0, 0, 0, 0, 0, 1, 1],
[1, 0, 0, 0, 1, 1, 1, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]
find_path(5, 8)
printMaze(maze)
输出
2 1 0 1 0 0 0 0
2 1 0 1 0 1 1 0
2 1 0 1 1 1 0 0
2 1 2 2 2 2 2 1
2 2 2 1 1 1 2 2
前四层的递归调用过程:

本文介绍了两种走迷宫的方法,一种利用栈进行回溯控制,另一种采用递归策略。在迷宫表示为二维数组的情况下,通过尝试错误和回溯找到出口。文中给出了Python2.7的实现代码,并展示了不同方法的输出结果。
2767

被折叠的 条评论
为什么被折叠?



