【问题标题】:Maze recursion solving StackOverflow error迷宫递归解决StackOverflow错误
【发布时间】:2012-07-13 21:08:09
【问题描述】:

我正在尝试使用递归解决迷宫。它被声明为Cell [][] maze

public class Cell {
    private Wall left;
    private Wall right;
    private Wall up;
    private Wall down;
    private boolean end;

    // Setters and getters not shown
}

如果单元格的某侧没有Wall,则它的值为null,否则它引用Wall 对象。 Wall 引用是一致的:与单个墙相邻的两个单元格都使用适当的字段引用它。如果缺少一堵墙,则两个相邻单元格都有对应的null 条目。这是搜索:

public boolean solveMaze(Cell[][] maze, int i, int j) {

    if (maze[i][j].isEnd()){
        System.out.println(maze[i][j].toString());
        return true;
    }
    if (maze[i][j].getDown() == null) {
        return solveMaze(maze, i, j + 1); 
    }
    if (maze[i][j].getUp() == null) {
        return solveMaze(maze, i, j - 1) ;
    }
    if (maze[i][j].getLeft() == null) {
        return solveMaze(maze, i - 1, j);
    }
    if (maze[i][j].getRight() == null) {
        return solveMaze(maze, i + 1, j) ;  
    }
    return false;
}

我收到 Stack Overflow 错误。我的递归停止条件有什么问题?

更新:

在您高度赞赏的帮助下,我解决了这个问题:这是完美无缺的正确解决方案:

public boolean solveMaze(Cell[][] maze, int i, int j){

    if (maze[i][j].isEnd()){
        System.out.println("Maze Exit :["+i+","+j+"]" );
        return true;
    }

    if (maze[i][j].isVisited()){
        return false;
    }

    maze[i][j].setVisited(true);

    if ((maze[i][j].getButtom() == null) ){
        if (solveMaze(maze,i,j+1)==true)
            return true;
    }

    if ((maze[i][j].getUp() == null) ){
    if ( solveMaze(maze,i,j-1) ==true )
        return true;
    }

    if ((maze[i][j].getLeft() == null)){
        if (solveMaze(maze,i-1,j))
            return true;
    }

    if ((maze[i][j].getRight() == null)){
        if (solveMaze(maze,i+1,j)) 
            return true;
    }

    maze[i][j].setVisited(false);
    return false;
} 

未来可能会对任何机构有所帮助。

【问题讨论】:

  • “getButton()”应该是“getBottom()”吗?
  • @millimoose 这显然是一个面试问题。那些怎么能得到应聘的呢?
  • @millimoose,我不明白你为什么称它为作业,我给出的解决方案不起作用,我需要帮助。对不起,你的评论发错地方了!!!
  • @user992484,这不是面试题,我把它标记为面试题,因为这样的问题在面试中经常被问到。无论如何,我删除了那个标签。
  • ;) 不是问题。好问题,期待答案

标签: java algorithm maze


【解决方案1】:

如果迷宫有一个循环,求解器可以永远围绕这个循环运行,这将导致您看到的堆栈溢出。您需要一种方法来确定您何时看到一个已经看过的迷宫广场。在这种情况下,您应该立即回溯。

这可以通过将每个单元格中的布尔标志 visited 初始设置为 false 然后为您搜索的每个方格设置为 true 来完成,或者您可以维护一个单独的 Set (i,j) 对已搜索,最初是空的。

注意:您对ij 的使用非常规。如果其他人使用常规用法编写迷宫阅读代码,这可能会导致问题。在数学中,i 通常用于行号,j 用于列。使用此约定,您的墙壁测试与您的增量和减量不一致。例如,缺少底墙将需要您增加 i

【讨论】:

  • 如果我遇到了死胡同,我需要回到上一个单元格,但它已经被标记为已访问,我该怎么办?
  • 你好@danny.lesnik。如果存在路径,该算法将始终找到路径。它可能找不到最短路径。事实上,它不太可能这样做。这称为深度优先搜索。
【解决方案2】:

在我看来,您的求解器方法似乎在绕圈子。
我建议你熟悉Breadth-First Search,它通常用于不太大的状态搜索问题。

如果你有一些关于如何搜索迷宫的“知识”,一种启发式方法,那么你也可以看看A-Star Search


BFS 在您的情况下可以做的如下:(顺便说一句,请善待并使用适当的构造器、获取器和设置器)

public class Cell {
    public int x;
    public int y;
    public Cell parent;

    @Override
    public boolean equals(Object obj) {
        // TODO Override equals so it only incudes x and y coorinates, and not parent
        return true;
    }

    @Override
    public int hashCode() {
        // TODO Override hash code as well
        return 0;
    }
}

public Cell seachFor(Cell start, Cell finish) {
    Queue<Cell> open = new LinkedList<>();
    Set<Cell> closed = new HashSet<>();

    open.add(start);

    while (!open.isEmpty()) {
        Cell current = open.poll();
        if (current.equals(finish)) {
            return current;
        }

        closed.add(current);

        for (Cell neighbour : getNeighbours(current)) {
            if (!closed.contains(neighbour)) {
                open.add(neighbour);
            }
        }
    }

    return null;

}

private List<Cell> getNeighbours(Cell current) {
    /* TODO Set the neighbour's "parent"
     * Return valid adjacent neighbour cells
     */
    return null;
}

public Deque<Cell> pathfinder(Cell start) {
    Deque<Cell> path = new ArrayDeque<>();
    path.push(start);

    Cell current = start;

    while (current.parent != null) {
        current = current.parent;
        path.push(current);
    }

    return path;
}

public static void main(String[] args) {
    Cell start = maze.getStart();
    Cell finish = maze.getFinish();
    Deque<Cell> path = pathFinder(searchFor(start, finish))
    while (!path.isEmpty()) {
        Cell current = path.pop();
        maze.moveTo(current);
    }
}

请注意,这是一个模拟代码,您需要在它工作之前对其进行优化。

【讨论】:

  • 谢谢 我知道这个算法,但我想用递归来解决它。
  • @danny.lesnik A* 或 BFS 当然可以递归编码,但图上的 BFS 更容易迭代地正确执行。您也可以尝试使用深度优先进行迭代深化,我认为这实际上是更可取的递归实现,永远不会无限递归,并且会找到最佳解决方案。
  • 此外,如果迷宫位于欧几里得平面上,并且您知道目标单元格在哪里,那么您总是有 A* 的启发式。 (曼哈顿距离或笛卡尔距离应该有效。)
【解决方案3】:

您的代码中没有任何内容可以检测您以前去过的地方。如果您的迷宫包含任何可以绕圈或循环回到您以前去过的地方而无需回溯的任何通道,它将继续无限地沿着相同的路径递归。

【讨论】:

    【解决方案4】:

    首先你要下去,直到你到达一堵墙。然后,一个向上 - 然后再次向下。一个向上,一个向下 - 直到 java 检测到这很愚蠢并以 StackOverFlowError 停止;)

    【讨论】:

      【解决方案5】:

      您在堆栈上分配的内存不足。也就是在给定时间超出 JVM 资源的情况下,您的递归倍增太快。

      您可以增加内存,然后终止此方法的所有其他迭代,或者更改您的算法。更改堆栈大小将是一个糟糕的解决方案,但即使您的代码有缺陷,它也可能有效howto increase the stacksize in eclipse 我建议您标记您已经访问过的区域

      迷宫是Cell[][] maze

        public class Cell{
          boolean visited = false;
          Wall left;
          Wall right;
          Wall up;
          Wall bottom;
          boolean end;
      
          //Settters and Getters
          }
      

      每次点击时将访问变量设置为 true,然后将代码更改为

      public boolean solveMaze(Cell[][] maze, int i, int j){
              if (maze[i][j].isEnd()){
                  System.out.println(maze[i][j].toString());
                  return true;
              }
              if (maze[i][j].getButton() == null)&&(maze[i][j].visited==false)){
                  return solveMaze(maze,i,j+1); 
              }
      
              if (maze[i][j].getUp() == null){
              return solveMaze(maze,i,j-1) ;
              }
              // ect 
      

      除非您自己解决问题,否则我不会实施它一个答案,您可能能够重复相同问题的答案,但您可能无法使用类似策略对类似问题提出新的答案。

      【讨论】:

      • 如果我遇到了死胡同,我需要回到上一个单元格,但它已经被标记为已访问,我该怎么办?在这种情况下,我是否需要将以前的单元格标记为未访问?
      • 我想说不,我相信在每个单元格中,您都会看到可以走的路并递归地检查所有方向。因此,制造它的人将完成。所以你从一个点出发,朝各个方向去寻找出路。如果这是一个你只允许一次检查一种方式的游戏,比如 ACSL,那么是的,你必须将它们标记为未访问,或者有一个扫雷系统,你可以在每个节点和回溯中放置多少未探索的路径与一些未探索的路径。
      猜你喜欢
      • 2014-12-11
      • 1970-01-01
      • 2014-05-08
      • 2014-05-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-05
      • 2019-03-18
      相关资源
      最近更新 更多