如何使用BFS查找迷宫中的最短路径?

时间:2019-07-25 12:26:28

标签: python python-3.x

我正在尝试找到解决迷宫的方法。我的老师说我必须使用BFS作为学习的方式。所以我自己做了算法,但是我不明白如何从中获得最短的路径。我已经看过其他人的代码,他们说回溯是做到这一点的方法。回溯是如何进行的,您又回溯了什么?

我之所以提供代码,是因为我喜欢它的反馈,也许我犯了一些错误:

def main(self, r, c):
    running = True
    self.queue.append((r, c))
    while running:
        if len(self.queue) > 0:  
            self.current = self.queue[0]
            if self.maze[self.current[0] - 1][self.current[1]] == ' ' and not (self.current[0] - 1, self.current[1])\
                    in self.visited and not (self.current[0] - 1, self.current[1]) in self.queue:
                self.queue.append((self.current[0] - 1, self.current[1]))
            elif self.maze[self.current[0] - 1][self.current[1]] == 'G':
                return self.path

            if self.maze[self.current[0]][self.current[1] + 1] == ' ' and not (self.current[0], self.current[1] + 1) in self.visited\
                    and not (self.current[0], self.current[1] + 1) in self.queue:
                self.queue.append((self.current[0], self.current[1] + 1))
            elif self.maze[self.current[0]][self.current[1] + 1] == 'G':
                return self.path

            if self.maze[self.current[0] + 1][self.current[1]] == ' ' and not (self.current[0] + 1, self.current[1]) in self.visited\
                    and not (self.current[0] + 1, self.current[1]) in self.queue:
                self.queue.append((self.current[0] + 1, self.current[1]))
            elif self.maze[self.current[0] + 1][self.current[1]] == 'G':
                return self.path

            if self.maze[self.current[0]][self.current[1] - 1] == ' ' and not (self.current[0], self.current[1] - 1) in self.visited\
                    and not (self.current[0], self.current[1] - 1) in self.queue:
                self.queue.append((self.current[0], self.current[1] - 1))
            elif self.maze[self.current[0]][self.current[1] - 1] == 'G':
                return self.path

            self.visited.append((self.current[0], self.current[1]))
            del self.queue[0]
            self.path.append(self.queue[0])

作为迷宫,我使用这样的东西:

############
# S        #
##### ######
#          #
######## ###
#          #
## ##### ###
#         G#
############

其中存储在矩阵中

我最终想要的只是列表内最短的路径作为输出。

2 个答案:

答案 0 :(得分:0)

由于这是编码任务,因此我将把代码留给您,并在这里简单地解释通用算法。

您有一个n×m的网格。我假设这是提供给您的。您可以将其存储在二维数组中。

第1步)创建一个与网格大小相同的新二维数组,并使用无效的坐标填充每个条目(取决于您,可以使用None或另一个值来指示到该坐标尚未被发现)。我将这个二维数组称为路径矩阵,将迷宫称为网格。

步骤2)入队起始坐标并更新该位置处的路径矩阵(例如,如果坐标(1,1)是起始位置,则更新矩阵[1,1])。

第3步)如果不在最终坐标处,请从队列中取出一个元素。对于从出列坐标开始的每个可能方向,检查其是否有效(没有壁且该坐标尚不存在该坐标),然后将所有有效坐标放入队列。

步骤4)重复步骤3。

如果有一条到达最终坐标的路径,则不仅可以使用此算法找到它,而且它也是最短的路径。要回溯,请在最终坐标的位置检查矩阵。这将导致您到达另一个坐标。继续此过程并 backtrack 直到到达起始坐标。如果您存储此回溯坐标列表,那么您将拥有一条反向的路径。

答案 1 :(得分:0)

代码中的主要问题是以下行:

self.path.append(self.queue[0])

当您以BFS方式向所有可能的方向行驶时,这只会继续添加到路径中。该路径最终将获得您访问的所有坐标,而这并不是真正的“路径”,因为使用BFS时,您会不断切换到搜索中的另一个分支,因此最终会收集完全不相关的位置。

您需要以其他方式构建路径。一种有效的内存存储方式是跟踪您访问节点时来自何处。您可以为此使用visited变量,但是将其设置为字典,该字典将为每个r,c对存储访问该单元格的r,c对。这就像建立一个链表。在每个新访问的单元格中,您都可以找到自己的来源,一直回到起始单元格。因此,当您找到目标时,您可以从此链接列表中构建路径。

您的代码中还有其他一些次要的问题:

  • 您不检查坐标是否有效。如果网格完全由#字符包围,那么这并不是真正的问题,但是如果边界处有间隙,则会出现异常

  • 四个方向的每个方向都有代码重复。尝试避免这种重复,将self.current[1] - 1之类的循环表达式存储在变量中,并在四个可能的方向上创建循环。

  • 变量running毫无意义:它永远不会变成False。而是将循环条件设为当前的下一个if条件。只要队列不为空,请继续。如果队列变空,则意味着没有到目标的路径。

  • 您将所有信息存储在self属性中。您仅应针对搜索后仍然相关的信息进行此操作。相反,我只是为queuevisitedcurrent,...等创建局部变量。

这是代码的外观:

class Maze():
    def __init__(self, str):
        self.maze = str.splitlines()

    def get_start(self):
        row = next(i for i, line in enumerate(self.maze) if "S" in line)
        col = self.maze[row].index("S")
        return row, col

    def main(self, r, c):
        queue = [] # use a local variable, not a member
        visited = {} # use a dict, key = coordinate-tuples, value = previous location
        visited[(r, c)] = (-1, -1)
        queue.append((r, c))
        while len(queue) > 0: # don't use running as variable
            # no need to use current; just reuse r and c:
            r, c = queue.pop(0) # you can remove immediately from queue
            if self.maze[r][c] == 'G':
                # build path from walking backwards through the visited information
                path = []
                while r != -1:
                    path.append((r, c))
                    r, c = visited[(r, c)]
                path.reverse()
                return path
            # avoid repetition of code: make a loop
            for dx, dy in ((-1, 0), (0, -1), (1, 0), (0, 1)):
                new_r = r + dy
                new_c = c + dx
                if (0 <= new_r < len(self.maze) and 
                        0 <= new_c < len(self.maze[0]) and 
                        not (new_r, new_c) in visited and
                        self.maze[new_r][new_c] != '#'):
                    visited[(new_r, new_c)] = (r, c)
                    queue.append((new_r, new_c))

maze = Maze("""############
# S        #
##### ######
#          #
######## ###
#          #
## ##### ###
#         G#
############""")

path = maze.main(*maze.get_start())
print(path)

看到它在repl.it上运行