【问题标题】:Shortest distance in maze using depth first search使用深度优先搜索在迷宫中的最短距离
【发布时间】:2018-12-12 22:58:06
【问题描述】:

给定一个 MxN 矩阵,其中每个元素可以是 'o'、's' 或 'g'('s' 和 'g' 是唯一的。只有一个起点和一个终点 )。

假设起始单元格“s”始终位于 (0,0)。

我们希望找到起始单元格 's' 到目标单元格 'g' 之间的最短距离,同时避开障碍物 'o'。

例子:

['s', ' ', ' ', ' ', ' ']
[' ', ' ', 'o', 'o', 'o']
['o', ' ', 'o', ' ', ' ']
[' ', ' ', ' ', 'o', ' ']
[' ', ' ', ' ', 'g', ' ']

从's'到'g'的最短距离是7。

我知道我们可以使用广度优先搜索hadlock算法轻松解决这个问题。但是,我很难理解为什么我的深度优先搜索不起作用

我正在用 Python 编写代码,代码如下。

class Solution:
   :type maze: list of list
   :type start: tuple
   :type end: tuple
   :rtype: int
   def findShortestDistance(self, maze, start, end):
      self.shortest=math.inf

      #set the default value of visited to be False
      self.visited=defaultdict(lambda: False)

      self.maze=maze
      self.rows=len(maze)
      self.cols=len(maze[0])
      self.depthFirstSearch(0,0,0)
      return self.shortest

   def depthFirstSearch(self, i, j, numStep):
      if i<0 or j<0 or i>=self.rows or j>=self.cols:
         return
      elif self.maze[i][j]=='o':
         return
      elif self.maze[i][j]=='g':
         self.shortest=min(self.shortest,numStep)
         return
      elif self.visited[(i,j)]:
         return

      self.visited[(i,j)]=True

      self.depthFirstSearch(i-1,j,numStep+1)
      self.depthFirstSearch(i,j-1,numStep+1)
      self.depthFirstSearch(i,j+1,numStep+1)
      self.depthFirstSearch(i+1,j,numStep+1)

      self.visited[(i,j)]=False

我真的不明白为什么这不起作用,但我无法通过该问题的几个隐藏测试用例。

另外,谁能告诉这个算法的运行时间?在我看来,它是指数级的。

【问题讨论】:

    标签: algorithm depth-first-search breadth-first-search


    【解决方案1】:

    DFS 在这里不是一个好主意,因为您将不断地重新访问相同的子路径,并且还因为您将不得不探索所有可能的路径以找到最短的路径。一般来说,当您遇到重复工作的递归问题时,您应该考虑动态编程。不过,在这种特定情况下,您可以使用 DFS,这实际上与使用标准 DP 解决方案解决此问题的方法非常相似。

    现在关于您的实施的一些说明:

    • 一般避免突变,尤其是在函数算法中。有一个带有副作用而不是返回值的递归函数有点奇怪,尽管它可以说有助于减少堆栈的大小。
    • 我发现计算复杂度非常困难。它基本上等于任意长度的有效路径的数量,因此它非常庞大,尤其是在障碍物很少的情况下,因为有许多长度大致等于 n*m 的路径。
    • 我找不到您的逻辑本身的问题。您确定它不只是在失败的测试中超时吗?

    【讨论】:

    • 我明白了。是的,这是有道理的。我怀疑这可能是由于高度复杂性,它只是在大量测试输入上超时。
    【解决方案2】:

    您的逻辑问题是您将节点标记为未访问会增加不必要的搜索,可以肯定,如果 A 点和 Dest 之间的最短浴不能长于 B 和 Dest 之间的最短路径路过A

    使用下面的

    class Solution:
       def findShortestDistance(self, maze, start, end):
            self.shortest=math.inf
    
            #set the default value of visited to be False
            self.visited=defaultdict(lambda: False)
    
            self.maze=maze
            self.rows=len(maze)
            self.cols=len(maze[0])
            self.depthFirstSearch(0,0,0)
            return self.shortest
    
       def depthFirstSearch(self, i, j, numStep):
            if i<0 or j<0 or i>=self.rows or j>=self.cols:
                return
            elif self.maze[i][j]=='o':
                return
            elif self.maze[i][j]=='g':
                self.shortest=min(self.shortest,numStep)
                return
            elif self.visited[(i,j)]:
                return
    
            self.visited[(i,j)] = True
            print("{} {}".format(i,j))
            self.depthFirstSearch(i-1,j,numStep+1)
            self.depthFirstSearch(i,j-1,numStep+1)
            self.depthFirstSearch(i,j+1,numStep+1)
            self.depthFirstSearch(i+1,j,numStep+1)
    
            return
    

    【讨论】:

    • 并非如此。考虑这个 ` [' s ', ' ', ' '] [' o ', 'o', ' '] [' g ', ' ', ' '] `因为终点可以在任何地方,障碍物可以在任何地方,我们都需要转弯才能向左走,或者去上面。
    • @fzy1995 感谢您澄清这一点,我现在更好地理解了问题并修改为添加了向上和向左遍历,但是将节点标记为未访问会导致无休止的循环搜索,可以肯定的是如果 A 点和 Dest 之间的最短浴池不能长于 B 和 Dest 之间经过 A 的最短路径。
    • 我不同意你的复杂性。有些路径的长度可以在O(M*N) 中,并且有无数的路径(我现在找不到计算它们的公式),所以它必须比O(N*M) 大很多。我也看不到 OP 的代码在哪些情况下会导致无限循环。每个递归调用都必须在某个点停止,因为有效位置的数量是有限的。由于在每次递归调用之后有效位置的数量都会严格减少,并且为没有更多有效移动的情况定义了结束条件,因此可以肯定地说它总是会结束
    • @Dici 谢谢你的解释。
    • Np,但不要相信我的话!如果你确实找到了一个无限循环的例子,我很想看看。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-08-25
    • 1970-01-01
    • 2021-01-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多