給定一個迷宮,用數組表示,其中0表示沒有障礙,1表示有障礙,尋求一個從左上角到右下角的路徑。node
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。python
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
能夠使用深度優先搜索來尋找一條路徑,這條路徑並不必定是最短路徑。它使用是一種回溯的方法,若是沒有遇到障礙,那麼就一直向前尋找,直到路走不通,那麼回溯到剛開始進入這條路徑的節點。算法
具體的作法是使用一個棧來保存第一個節點的信息,而後棧不爲空的時候進入循環:不斷的尋找能夠行走的路徑存入到棧當中,當發現沒有可走的路徑的時候出棧。最後若是到達目的節點,由於棧中前一個元素是後一個元素的前驅節點,因此棧中保存的內容就是一條路徑。數組
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
寬度優先搜索的策略是一步一步不斷的向外擴展,而不是像深度優先搜索同樣沿一條路徑一直探尋,探尋不到的時候回溯。因此,若是寬度優先搜索到達終點,那麼它所探尋的路徑必定是最短的。app
寬度優先搜索須要結合隊列來使用,它不像棧那樣能夠直接保存前驅節點的信息,因此還須要記錄節點的前驅節點。這裏使用一個和迷宮同樣大的數組來保存前驅節點的信息,也能夠使用字典來保存這一信息。code
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)