迷宫问题
问题描述
给定一个迷宫,用数组表示,其中0表示没有障碍,1表示有障碍,寻求一个从左上角到右下角的路径。
maze = [[0, 1, 0, 0, 0],
[0, 1, 0, 1, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 1, 0]]
路径是否存在的问题(使用递归的方法来判断)
假如只是寻找是不是存在一个路径,这个时候可以使用递归的算法。
递归算法其实是将所有的可能路径都试探一下,只要一条路径试探成功,那么就会返回True,当所有路径都不成功的时候返回False。
def find_maze(maze, i, j, row, col):
"""
Args:
maze: 迷宫(二维数组)
i,j: 路径开始的坐标,为(0,0)
row,col: 矩阵的长和宽,也是路径结束的坐标
Returns:
boolean: 是否存在路径
"""
if i < 0 or i >= row or j < 0 or j >= col or maze[i][j] != 0:
return False
maze[i][j] = 2 # 标记表示已经走过
if i == row-1 and j == col-1:
return True # 已经成功到达
if (find_maze(maze, i - 1, j, row, col) or
find_maze(maze, i + 1, j, row, col) or
find_maze(maze, i, j + 1, row, col) or
find_maze(maze, i, j - 1, row, col)):
return True
return False
使用深度优先搜索(DFS)寻找一条路径
可以使用深度优先搜索来寻找一条路径,这条路径并不一定是最短路径。它使用是一种回溯的方法,如果没有遇到障碍,那么就一直向前寻找,直到路走不通,那么回溯到刚开始进入这条路径的节点。
具体的做法是使用一个栈来保存第一个节点的信息,然后栈不为空的时候进入循环:不断的寻找可以行走的路径存入到栈当中,当发现没有可走的路径的时候出栈。最后如果到达目的节点,因为栈中前一个元素是后一个元素的前驱节点,所以栈中保存的内容就是一条路径。
def find_available_point(maze, i, j, row, col):
"""一旦发现一个当前可以到达的节点就返回"""
if i+1 < row and maze[i+1][j] == 0:
return [i+1, j]
if i-1 >= 0 and maze[i-1][j] == 0:
return [i-1, j]
if j + 1 < col and maze[i][j+1] == 0:
return [i, j+1]
if j - 1 >= 0 and maze[i][j-1] == 0:
return [i, j-1]
return [-1, -1]
def find_maze_use_stack_with_path(maze, i, j, row, col):
"""使用栈来进行回溯法,当栈不为空的时候进行循环
循环的内容是:将栈顶的元素弹出来,然后将所有可能的结果进入栈中
"""
path = [[i,j]]
maze[i][j] = 2
while path:
i, j = path[-1]
if i == row-1 and j == col - 1:
return path
available_point = find_available_point(maze, i, j, row, col)
if available_point[0] != -1:
maze[available_point[0]][available_point[1]] = 2
path.append(available_point)
else:
path.pop()
return path
使用宽度优先搜索(BFS)得到最短的路径
宽度优先搜索的策略是一步一步不断的向外扩展,而不是像深度优先搜索一样沿一条路径一直探寻,探寻不到的时候回溯。所以,如果宽度优先搜索到达终点,那么它所探寻的路径一定是最短的。
宽度优先搜索需要结合队列来使用,它不像栈那样可以直接保存前驱节点的信息,所以还需要记录节点的前驱节点。这里使用一个和迷宫一样大的数组来保存前驱节点的信息,也可以使用字典来保存这一信息。
from queue import Queue
def find_all_available_point(maze, i, j, row, col):
"""找到当前节点可以一步到达的所有节点"""
result = []
if i+1 < row and maze[i+1][j] == 0:
result.append([i+1, j])
if i-1 >= 0 and maze[i-1][j] == 0:
result.append([i-1, j])
if j + 1 < col and maze[i][j+1] == 0:
result.append([i, j+1])
if j - 1 >= 0 and maze[i][j-1] == 0:
result.append([i, j-1])
return result
def get_path(pre_nodes, i, j):
"""根据前驱节点来得到最终的路径,i,j为终节点
初始节点的前驱节点为它本身,可作为递归结束的条件
"""
path = [[i, j]]
while pre_nodes[i][j] != [i, j]:
i, j = pre_nodes[i][j]
path.append([i,j])
return path
def find_maze_use_queue_with_path(maze, i, j, row, col):
q = Queue()
pre_nodes = [[0 for x in range(col)] for x in range(row)]
pre_nodes[i][j] = [i, j]
maze[i][j] = 2
q.put([i, j])
while not q.empty():
i, j = q.get()
if i == row-1 and j == col-1:
path = get_path(pre_nodes, i, j)
path = path[::-1] # 翻转一下
return path
available_points = find_all_available_point(maze, i, j ,row, col)
if available_points:
for available_point in available_points:
pre_nodes[available_point[0]][available_point[1]] = [i, j] # 记录前驱节点
maze[available_point[0]][available_point[1]] = 2
q.put(available_point)
return []
if __name__ == '__main__':
maze = [[0, 1, 0, 0, 0],
[0, 1, 0, 1, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 1, 0]]
x = find_maze_use_queue_with_path(maze, 0 ,0 , 5, 5)
print(x)