为什么我的递归迷宫算法没有回溯?

时间:2014-06-14 21:53:01

标签: java algorithm recursion libgdx path-finding

原谅我,我是递归的新手,但据我所知,这应该有效,但事实并非如此。我做了这个方法,当成功找到路径时递归调用自己:

private void RandStep(Point pos)
    {       
        Collections.shuffle(Arrays.asList(directions), rand); //Incorrect, should have a local directions array. Nothing to do with the question though.
        for (int i = 0; i < directions.length;i++)
        {
            //Check if the positions are within map bounds.
            if (pos.x + directions[i].x >= 0 && pos.x + directions[i].x < width && pos.y + directions[i].y >= 0 && pos.y + directions[i].y < height)
            {
                //Check if the position is unvisited.
                if (!mazeMap[pos.x + directions[i].x][pos.y + directions[i].y].visited)
                {
                    //Break walls this tile.
                    CarvePassage(pos, directions[i]);
                    mazeMap[pos.x + directions[i].x][pos.y + directions[i].y].visited = true;
                    position.setLocation(pos.x + directions[i].x, pos.y + directions[i].y);
                    RandStep(position);
                }
            }
        }
    }
  • 首先,它将4个方向的数组随机化。
  • 然后我遍历数组以找到可能的方向。
  • 然后检查找到的方向是否有效,否则它将转到数组中的下一个方向
  • 当它有效时,它会调用另一个方法来雕刻当前图块和下一个图块的墙。
  • 它将当前位置更改为下一个并将其标记为已访问。
  • 最后,它再次召唤自己进行下一步。

这一切都有效,直到它第一次卡在被访问的单元格或地图边界之间。如果我理解正确的递归,它应该退出此方法并转到上一次运行RandStep并完成方向循环。当它在那里找不到任何有效的单元格时:它应该再次退出并完成前一个RandStep中的循环。这应该重复,直到完成第一次RandStep运行的方向循环。

就像我说的那样,它暂时无法找到任何有效的细胞。它不会在递归堆栈上继续以前的方法。

2 个答案:

答案 0 :(得分:3)

很简单,因为你不退后了!

position.setLocation(pos.x + directions[i].x, pos.y + directions[i].y);
RandStep(position);

应该是这样的:

position.setLocation(pos.x + directions[i].x, pos.y + directions[i].y);
RandStep(position);
position.setLocation(pos.x - directions[i].x, pos.y - directions[i].y);

作为一种直觉,想想在递归的基本情况下会发生什么。你周围的所有瓷砖都被访问过,你处于死路。这种情况看起来像:

 _
| |
|x|

x =&#34;你在这里&#34;)

然后,position.setLocation(pos.x + directions[i].x, pos.y + directions[i].y);将你带到这里:

 _
|x|
| |

然后,RandStep(position);无法 ,因为您周围的所有位置都会被探索。所以接下来要做的就是退步,这可以通过以下方式实现:position.setLocation(pos.x - directions[i].x, pos.y - directions[i].y);

答案 1 :(得分:1)

让我们仔细看看幕后发生的事情。假设您熟悉堆和堆栈的内容,变量position位于heap,因为它不是函数RandStep的本地变量。另一方面,pos是本地的,因此它驻留在堆栈上。每次递归调用时,都会分配一个新的堆栈帧(从frameA到frameC)。为简洁起见,我将头寸表示为单一值。让我们说你从位置0开始,然后将墙壁向位置1突破。会发生的是为pos的新值分配新帧,但会覆盖position 。当你从位置1转换到位置2时也一样。让我们说从位置2那里无处可去,所以我们需要追溯。然后取消分配frameC。但是,不是在顶部框架(现在是frameB)中使用pos的值,而是在RandStep上调用position,因此您使用的值是在堆上,尽管帧取消分配,但仍保持不变。

&#34;退后&​​#34;正如Intredasting所暗示的那样,意味着手动更新堆,以便position遵循与pos相同的演变。在我看来,这违背了递归的目的。它的美妙之处在于它通过释放来照顾踩踏部分。

总之,您需要做的是避免在递归函数中更改堆。

|STACK          |   |STACK         |    |STACK          |
|               |   |              |    |               |
|               |   |              |    |frameC: pos = 2|---> got stuck, deallocate frame
|               |   |frameB: pos=1 |    |frameB: pos = 1|     and backtrack
|frameA: pos=0  |   |frameA: pos=0 |    |frameA: pos = 0|
|---------------|   |--------------|    |---------------|
|HEAP           |   |HEAP          |    |HEAP           |
|position = 0   |   |position=1    |    |position = 2   |
|               |   |              |    |               |
|               |   |              |    |               |
-----------------   ----------------    -----------------