了解回溯(迷宫算法)

时间:2014-05-17 14:57:14

标签: java algorithm recursion backtracking maze

我试图通过创建一种从迷宫中寻找出路的算法来理解递归回溯。所以这是我的“回溯”逻辑:

1)从ex:up,down之前确定你当前所在地的所有未开放地点。 (左侧和右侧可能被墙挡住或者之前可能已被访问过)

2)如果amtOfOpenLocations >= 1选择随机openLocation并移动到那里。

3)(递归调用)如果没有出错,请重复#1直到达到路径输出或基本情况。

4)如果amtOfMoveLocations < 1退回每次移动,直到其中一个移动的移动位置,而不是已经进行的任何移动。重复步骤1-3。

所以我的第一个问题是:这是一个正确的回溯算法实现吗?

如果我的逻辑是正确的,这是我的下一个问题:

所以基本上我所做的就是在找到出路之前,继续检查已经制作的位置。当我找到出路时,我会忽略所有其他可能的动作并返回解决方案。例如,如果我在位置(4,4)并且我的位置(4,3), (4,5), (5,4), (3,4)全部可用作openLocations我是否会对这些位置中的每一个进行4次递归调用,或者我只是简单地进行一次递归调用打电话给他们中的任何一个并逐个测试每个位置?

我的书上写着“如果你不在出口处,请进行4次递归调用,检查所有4个方向,输入4个相邻单元格的新坐标。”

关于类似问题的stackoverflow上的answer说:“每次迭代多次移动......是错误的” 因此,我很困惑。我的书错了还是什么?

最后,防止我之前检查过的地点的最佳方法是什么?我的想法是保留一个临时数组,其中所有访问过的位置都标有“!”并且我的getMoveLocations()方法会避免任何带有“!”的单元格。有没有更好的方法来做到这一点,还是我的方式可以接受?

2 个答案:

答案 0 :(得分:3)

  

这是回溯算法的正确实现吗?

是的,它看起来很好。

随机移动可能会增加代码的复杂性。如果做得不对,并不多,但仍然比确定性地向上,向下,向左,然后向右移动更复杂。

另外 - 如果你只有4个方向,那么步骤1作为一个单独的步骤可能是过度的 - 只是循环所有4个方向(确定性地,或者将它们全部添加到列表中并随机选择一个并将其移除直到列表为在递归调用之前进行访问/阻止检查应该更加简单。

  

我是否会对每个位置进行4次递归调用,或者我只是对其中任何一位进行一次递归调用并逐个测试每个位置?

我不确定你的意思是第二部分。 (在Java中)在代码中连续出现的递归调用将被串行执行。你会为每个人进行递归通话(如何通过1次通话访问4个位置?)。

  

关于类似问题的stackoverflow的答案说:“每次迭代多次移动......是错误的”

这听起来像是误入歧途的用户的随意。

虽然基本思想有一定意义 - 使用迭代版本,您在迭代中排队许多移动,但每次迭代只能执行一次(您pop)。使用递归版本,它不太明显。

查看Wikipedia上的深度优先搜索伪代码,它明确地使每次迭代多次递归调用/排队多次移动:

Recursive:
1  procedure DFS(G,v):
2      label v as discovered
3      for all edges from v to w in G.adjacentEdges(v) do
4          if vertex w is not labeled as discovered then
5              recursively call DFS(G,w)

Iterative:
1  procedure DFS-iterative(G,v):
2      let S be a stack
3      S.push(v)
4      while S is not empty
5            v ← S.pop() 
6            if v is not labeled as discovered:
7                label v as discovered
8                for all edges from v to w in G.adjacentEdges(v) do
9                    S.push(w)
  

阻止我之前检查过的地点的最佳方法是什么?

您的方法并不错,但更自然的方法是使用boolean数组,尽管上次检查时,这并不是特别有效的内存。最有效的内存是BitSet,尽管其代码稍微复杂一些。

答案 1 :(得分:1)

我会尝试着解决所有问题:

  1. 算法大纲看起来正确。第一步确保您在尝试仅未访问的位置时不会遇到无限回归。只要您有尝试的位置,您就继续前进。在步骤号。 3递归下降发生。第一步4是递归函数的基本情况。这应该首先进行测试,如果它是真的,你必须立即退出该功能。因此,一旦确定无法完成有效解决方案,它就“放弃每个部分候选人(”回溯“)。” (来自Wikipedia) 通过随机选择一个新的 openLocation ,您也会使其不确定,但仍然确定。这是一种随机的。

  2. 您是否对这些位置中的每一个进行了4次递归调用,或者您是否对其中任何一位进行了一次递归调用并逐个测试每个位置?嗯......你做后者,在你的“树”组合中测试每个位置。回溯基本上走了一棵可能的组合树,然后下到解决方案,如果它被卡住,它会向上移动。让我们将该树的水平部分称为层 你的书实现的方式与你的略有不同。虽然您有可以访问的位置列表,但您的图书的作者似乎并不喜欢。他们将尝试一个位置的所有四个直接邻居,如果它是一堵墙,“出了问题”(步骤3)并且他们追溯。在这种情况下,他们不需要一个列表,只需要四个递归调用,一个接一个。同时,您从可能位置的列表中选择一个随机项,这些项是事先为当前单元计算的。请确保每次从递归下降回来时都不会重新计算该列表。为了使这些列表永久化并且只是更新它们,它们不应该在递归函数中,而是在开始回溯之前预先计算,然后在每一步中更新它们。

  3. 要么在没有该列表的情况下尝试它,就像本书的作者一样,要么使用数组,这是正确的方法。我建议使用堆栈数据结构,这样您就可以简单地弹出一个新位置,直到它为空。