迷宫问题

问题描述

给定一个迷宫,用数组表示,其中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)
posted @ 2018-08-22 21:57  小舔哥  阅读(366)  评论(0编辑  收藏  举报