迷宫:寻找从起点到终点的最短路径

Maze: Finding the shortest path from start point to end point

我目前正在学习一些 CS 基础知识(一周前开始),我偶然发现了这个挑战。迷宫是一个列表列表,'#' 表示一堵墙,' ' 表示一条开放路径。我应该根据连续坐标 (col, row) 找到从左下角到右上角的最短路径。我必须在不导入任何东西的情况下创建一个函数。

例如:

maze = 
       [[' ', '#', ' ', '#', ' '],
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', ' ', ' ', ' ', ' '], 
        ['#', ' ', '#', ' ', ' '], 
        [' ', ' ', '#', '#', '#']]

首先我得到起点坐标(0, 9)和终点坐标(4, 0)。然后我需要找到最短路径。 预期结果为:[(0, 9), (1, 9), (1, 8), (1, 7), (2, 7), (3, 7), (4, 7), (4, 6), (4, 5), (4, 4), (4, 3), (4, 2), (4, 1), (4, 0)].

这是我的代码:

def grid(maze):
    ''' Maze Properties'''
    num_rows = len(maze)
    num_cols = len(maze[0])
    end_pt = (num_cols - 1, 0)
    start_pt = (0, num_rows - 1)
    print(start_pt, end_pt)

    '''Recursive Function'''

    def find_shortest(maze, start_point, end_point, visited, shortest_path):
        '''BASE CASE'''
        if start_point == end_point:
            return shortest_path


        adj_points = []
        '''FIND ADJACENT POINTS'''
        current_row = start_point[1]
        current_col = start_point[0]

        #UP
        if current_row > 0:
            if maze[current_row - 1][current_col] == " ":
                adj_points.append((current_col, current_row - 1))

        #RIGHT
        if current_col < (len(maze[0])-1):
            if maze[current_row][current_col] == " ":
                adj_points.append((current_col + 1, current_row))
        #DOWN
        if current_row < (len(maze) - 1):
            if maze[current_row + 1][current_col] == " ":
                adj_points.append((current_col, current_row + 1))
        #LEFT
        if current_col > 0:
            if maze[current_row][current_col - 1] == " ":
                adj_points.append((current_col - 1, current_row))

        print(adj_points)
        if all(elem in visited for elem in adj_points):
            return

        '''LOOP THROUGH ADJACENT PTS'''
        for point in adj_points:
            if point not in visited:
                visited.append(point)
                shortest_path.append(point)
                return find_shortest(maze, point, end_point, visited, shortest_path)



    path = [start_pt]
    visited = [start_pt]
    shortest_path = find_shortest(maze, start_pt, end_pt, visited, path)
    return shortest_path

我认为问题是如果我遇到了死胡同,它应该回溯到最后一个可以选择的点,我不知道该怎么做。

注意:我相信这是 DFS,但 BFS 解决方案也将受到赞赏。

修改此处给出的post:https://www.techiedelight.com/find-shortest-path-in-maze/

更新:代码从 C++ 转换为 Python。逻辑还是一样。

M = 10 # Rows
N = 5 # Columns
def isSafe(grid,visited,x,y):
    if grid[x][y]=='#' or visited[x][y]==True:
        return False # Unsafe
    return True # Safe

def isValid(x,y):
    if x<M and y<N and x>=0 and y>=0:
        return True # Valid
    return False # Invalid

def solve(grid,visited,i,j,dest_x,dest_y,curr_dist,min_dist,shortestPath,currentPath):
    if i==dest_x and j==dest_y: # if destination is found, update min_dist
        if curr_dist<min_dist[0]: # If a shorter distance is found
            min_dist[0] = curr_dist # Update distance
            shortestPath.clear() # Update path
            shortestPath.extend(currentPath)
            shortestPath.append((dest_y,dest_x)) # Push the destination coordinates
        return

    # set (i, j) cell as visited
    visited[i][j] = True
    currentPath.append((j,i))

    # go to bottom cell
    if isValid(i + 1, j) and isSafe(grid,visited,i + 1, j):
        solve(grid,visited,i + 1, j, dest_x, dest_y, curr_dist + 1,min_dist,shortestPath,currentPath)
    # go to right cell
    if isValid(i, j + 1) and isSafe(grid,visited,i, j + 1):
        solve(grid,visited,i, j + 1, dest_x, dest_y, curr_dist + 1,min_dist,shortestPath,currentPath)

    # go to top cell
    if isValid(i - 1, j) and isSafe(grid,visited,i - 1, j):
        solve(grid,visited,i - 1, j, dest_x, dest_y, curr_dist + 1,min_dist,shortestPath,currentPath)

    # go to left cell
    if isValid(i, j - 1) and isSafe(grid,visited,i, j - 1):
        solve(grid,visited,i, j - 1, dest_x, dest_y, curr_dist + 1,min_dist,shortestPath,currentPath)
    visited[i][j] = False
    currentPath.pop()

if __name__ == "__main__": 
    min_dist = [9e10] # Start from infinity
    shortestPath = [] # It will contain the path (y,x) tuples
    currentPath = [] # It will store the current path temporarily
    grid = [
        [' ', '#', ' ', '#', ' '],
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', ' ', ' ', ' ', ' '], 
        ['#', ' ', '#', ' ', ' '], 
        [' ', ' ', '#', '#', '#']
    ]
    visited = []
    for i in range(M):
        _list = list()
        for j in range(N):
            _list.append(False)
        visited.append(_list)
    solve(grid,visited,M-1,0,0,N-1,0,min_dist,shortestPath,currentPath)
    print("Minimum distance: ",min_dist[0])
    print("Path: [",end=" ")
    for path in shortestPath:
        print('('+str(path[0])+','+str(path[1])+')',end=' ')
    print("]")

输出

Minimum distance:  13
Path: [ (0,9) (1,9) (1,8) (1,7) (2,7) (3,7) (4,7) (4,6) (4,5) (4,4) (4,3) (4,2) (4,1) (4,0) ]

您的 DFS 方法 returns 该特定迷宫的最短路径,因为它恰好首先选择了正确的方向,但它不会找到其他一些迷宫的最短路径。例如,对于这个迷宫:

maze = [[' ', ' ', ' ', '#', ' '],
        [' ', '#', ' ', '#', ' '],
        [' ', ' ', ' ', ' ', ' ']]

请注意,您的代码在 #RIGHT 情况下存在错误,其中缺少 + 1,因此实际上您的代码无法找到上述迷宫的路径。即使错误被纠正,它也会找到更长的路径,首先到达网格的左上角。

为了找到最短路径,最好使用 BFS,因为这样你就可以确定你第一次命中目标对应于最短路径。

以下是您在未进行不必要的更改的情况下适应 BFS 的代码。注意这里的visited不是一个列表,而是一个字典,它不仅会告诉你访问过一个广场,还会告诉你是从哪个广场来访问它的。

根据这条信息,您可以构建路径。

此外,我在这里选择从头到尾开始搜索,因为这样您就可以按照正确的顺序重建路径(展开回)。否则你必须在返回之前反转路径。

def grid(maze):
    ''' Maze Properties'''
    num_rows = len(maze)
    num_cols = len(maze[0])
    end_pt = (num_cols - 1, 0)
    start_pt = (0, num_rows - 1)

    '''BFS'''
    visited = {end_pt: None}
    queue = [end_pt]
    while queue:
        current = queue.pop(0)
        if current == start_pt:
            shortest_path = []
            while current:
                shortest_path.append(current)
                current = visited[current]
            return shortest_path
        adj_points = []
        '''FIND ADJACENT POINTS'''
        current_col, current_row = current
        #UP
        if current_row > 0:
            if maze[current_row - 1][current_col] == " ":
                adj_points.append((current_col, current_row - 1))
        #RIGHT
        if current_col < (len(maze[0])-1):
            if maze[current_row][current_col + 1] == " ": ## There was an error here!
                adj_points.append((current_col + 1, current_row))
        #DOWN
        if current_row < (len(maze) - 1):
            if maze[current_row + 1][current_col] == " ":
                adj_points.append((current_col, current_row + 1))
        #LEFT
        if current_col > 0:
            if maze[current_row][current_col - 1] == " ":
                adj_points.append((current_col - 1, current_row))

        '''LOOP THROUGH ADJACENT PTS'''
        for point in adj_points:
            if point not in visited:
                visited[point] = current
                queue.append(point)

maze = [[' ', '#', ' ', '#', ' '],
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', '#', ' ', '#', ' '], 
        [' ', ' ', ' ', ' ', ' '], 
        ['#', ' ', '#', ' ', ' '], 
        [' ', ' ', '#', '#', '#']]

print(grid(maze))