深度优先搜索的递归调用会导致栈溢出错误。

huangapple go评论76阅读模式
英文:

Depth First Search recursive calls cause StackOverflow error

问题

以下是您提供的内容的中文翻译:

我正在使用深度优先搜索算法的实现来解决迷宫问题我不希望找到最短路径而是希望找到找到有效路径的最快方法这是我用来解决迷宫问题的方法迷宫是2D整数数组其中0表示空白方块1表示墙壁2表示访问过的方块9表示目标

public class DepthAlgorithm {

    /**
     * 当找到路径时,此方法返回true。它还将路径填入列表中。它将从(xn,yn,.....,x2,y2,x1,y2)开始,因为它会使用递归。
     * @param maze 迷宫的二维数组
     * @param x 起始位置的x坐标
     * @param y 起始位置的y坐标
     * @param path 路径的列表
     * @return 如果找到路径则返回true
     */
    public static boolean searchPath(int [][] maze, int x, int y, List<Integer> path){
        // 检查是否到达目标(9)
        if (maze[y][x] == 9) {
            path.add(x);
            path.add(y);
            return true;
        }

        // 当前位置为未访问状态(0)时,将其标记为已访问(2)
        if (maze[y][x] == 0) {
            maze[y][x] = 2;

            // 在这里,我们递归地访问所有相邻方块,如果找到路径,我们将填充路径列表
            int dx = -1;                // 从起始位置开始搜索
            int dy = 0;                 // 位置为 (x-1,y)
            if (searchPath(maze, x + dx, y + dy, path)) {
                path.add(x);
                path.add(y);
                return true;
            }

            dx = 1;                    // 从起始位置开始搜索
            dy = 0;                    // 位置为 (x+1,y)
            if (searchPath(maze, x + dx, y + dy, path)) {
                path.add(x);
                path.add(y);
                return true;
            }

            dx = 0;                   // 从起始位置开始搜索
            dy = -1;                  // 位置为 (x,y-1)
            if (searchPath(maze, x + dx, y + dy, path)) {
                path.add(x);
                path.add(y);
                return true;
            }

            dx = 0;                   // 从起始位置开始搜索
            dy = 1;                   // 位置为 (x,y+1)
            if (searchPath(maze, x + dx, y + dy, path)) {
                path.add(x);
                path.add(y);
                return true;
            }
        }
        return false;
    }
}

我的算法对于小迷宫大小的问题效果很好当我想解决一个50 * 50的迷宫时速度相当快但当我转向500 * 500的迷宫时会出现堆栈溢出错误我理解这是因为函数的递归调用太多但我不知道如何解决**是否有另一种方法我仍然可以使用深度优先搜索并存储路径但不会出现堆栈溢出或者是否可以对代码进行一些更改以修复这个问题**

public class MazeRunner {

    // 迷宫是一个二维数组,必须在周边用墙壁填充,以使该算法能够正常工作。我们的起始位置在此情况下为(1,1),目标位置标记为9,即(11,8)。
    private int[][] maze ;
    private final List<Integer> path = new ArrayList<>();
    public long startTime,stopTime;

    public MazeRunner(int [][] maze){
        this.maze = maze;
    }

    public void runDFSAlgorithm(int startingX,int startingY){
        startTime = System.nanoTime();
        DepthAlgorithm.searchPath(maze,startingX,startingY,path);
        stopTime = System.nanoTime();
        printPath();
        System.out.println("深度优先算法用时: "+((double) (stopTime-startTime) / 1_000_000)+" 毫秒");

    }

    public void printPath(){
        ListIterator li = path.listIterator(path.size());
        int lengthOfPath = (path.size()/2-1);
        int fromX,fromY,bool = 0,toX = 0,toY = 0,i = 2;
        while(li.hasPrevious()){
            if (bool == 0) {
                fromX = (int) li.previous();
                fromY = (int) li.previous();
                toX = (int) li.previous();
                toY = (int) li.previous();
                System.out.println("从 ("+fromY+", "+fromX+") 到 ("+toY+", "+toX+")");
                bool++;
                continue;
            }
            if (bool == 1){
                fromX = toX;
                fromY = toY;
                toX = (int) li.previous();
                toY = (int) li.previous();
                System.out.println("从 ("+fromY+", "+fromX+") 到 ("+toY+", "+toX+")");
                i++;
            }
        }
        System.out.println("\n路径长度为: "+lengthOfPath);
    }

    public static void main(String[] args){
        int [][] maze = {{1,1,1,1,1,1,1,1,1,1,1,1,1},
                         {1,0,1,0,1,0,1,0,0,0,0,0,1},
                         {1,0,1,0,0,0,1,0,1,1,1,0,1},
                         {1,0,0,0,1,1,1,0,0,0,0,0,1},
                         {1,0,1,0,0,0,0,0,1,1,1,0,1},
                         {1,0,1,0,1,1,1,0,1,0,0,0,1},
                         {1,0,1,0,1,0,0,0,1,1,1,0,1},
                         {1,

<details>
<summary>英文:</summary>

I mam using an implementation of the Depth First Search Algorithm in order to solve a maze. I do not wish for the shortest path to be found but for the fastest way to find a valid path. This is the method i use to solve mazes. Mazes are 2d int arrays where 0 is an open square, 1 is a wall, 2 is a visited square and 9 is the destination.

        public class DepthAlgorithm {

    /**
     * This method returns true when a path is found. It will also fill up the
     * list with the path used. It will start from (xn,yn,.....,x2,y2,x1,y2)
     * because it will use recursion.
     * @param maze 2d array of maze
     * @param x the x of the starting position
     * @param y the y of the starting position
     * @param path a List of the path
     * @return True if a path is found
     */
    public static boolean searchPath(int [][] maze, int x, int y, List&lt;Integer&gt; path){
        // Check if the destination (9) is reached
        if (maze[y][x] == 9) {
            path.add(x);
            path.add(y);
            return true;
        }

        // When the current position is not visited (0) we shall make it visited (2)
        if (maze[y][x] == 0) {
            maze[y][x] = 2;

            //Here we visit all neighbour squares recursively and if a path is found
            // we shall fill the path list with the current position.
            int dx = -1;                // Start and search from starting
            int dy = 0;                 // position (x-1,y)
            if (searchPath(maze, x + dx, y + dy, path)) {
                path.add(x);
                path.add(y);
                return true;
            }

            dx = 1;                    // Start and search from starting
            dy = 0;                    // position (x+1,y)
            if (searchPath(maze, x + dx, y + dy, path)) {
                path.add(x);
                path.add(y);
                return true;
            }

            dx = 0;                   // Start and search from starting
            dy = -1;                  // position (x,y-1)
            if (searchPath(maze, x + dx, y + dy, path)) {
                path.add(x);
                path.add(y);
                return true;
            }

            dx = 0;                   // Start and search from starting
            dy = 1;                   // position (x,y+1)
            if (searchPath(maze, x + dx, y + dy, path)) {
                path.add(x);
                path.add(y);
                return true;
            }
        }
        return false;
    }
}

My algorithm works fine for small maze sizes. When i want to solve a 50 * 50 maze it&#39;s quite quick. When i move to 500 * 500 a Stack Overflow error shows up. I can understand that it shows up because of the many recursive calls of the function but i do not know how to fix it. **Is there another way so I can still use the Depth First Search and store my path but without the Stack Overflow? Or are there any changes that can be done in my code so it can be fixed?** 

    public class MazeRunner {

    // Maze is a 2d array and it has to be filled with walls peripherally
    // with walls so this algorithm can work. Our starting position in this
    // will be (1,1) and our destination will be flagged with a 9 which in
    // this occasion is (11,8).
    private int[][] maze ;
    private final List&lt;Integer&gt; path = new ArrayList&lt;&gt;();
    public long startTime,stopTime;

    public MazeRunner(int [][] maze){
        this.maze = maze;
    }

    public void runDFSAlgorithm(int startingX,int startingY){
        startTime = System.nanoTime();
        DepthAlgorithm.searchPath(maze,startingX,startingY,path);
        stopTime = System.nanoTime();
        printPath();
        System.out.println(&quot;Time for Depth First Algorithm: &quot;+((double) (stopTime-startTime) / 1_000_000)+&quot; milliseconds&quot;);

    }

    public void printPath(){
        ListIterator li = path.listIterator(path.size());
        int lengthOfPath = (path.size()/2-1);
        int fromX,fromY,bool = 0,toX = 0,toY = 0,i = 2;
        while(li.hasPrevious()){
            if (bool == 0) {
                fromX = (int) li.previous();
                fromY = (int) li.previous();
                toX = (int) li.previous();
                toY = (int) li.previous();
                System.out.println(&quot;From (&quot;+fromY+&quot;, &quot;+fromX+&quot;) to (&quot;+toY+&quot;, &quot;+toX+&quot;)&quot;);
                bool++;
                continue;
            }
            if (bool == 1){
                fromX = toX;
                fromY = toY;
                toX = (int) li.previous();
                toY = (int) li.previous();
                System.out.println(&quot;From (&quot;+fromY+&quot;, &quot;+fromX+&quot;) to (&quot;+toY+&quot;, &quot;+toX+&quot;)&quot;);
                i++;
            }
        }
        System.out.println(&quot;\nLength of path is : &quot;+lengthOfPath);
    }

    public static void main(String[] args){
        int [][] maze = {{1,1,1,1,1,1,1,1,1,1,1,1,1},
                         {1,0,1,0,1,0,1,0,0,0,0,0,1},
                         {1,0,1,0,0,0,1,0,1,1,1,0,1},
                         {1,0,0,0,1,1,1,0,0,0,0,0,1},
                         {1,0,1,0,0,0,0,0,1,1,1,0,1},
                         {1,0,1,0,1,1,1,0,1,0,0,0,1},
                         {1,0,1,0,1,0,0,0,1,1,1,0,1},
                         {1,0,1,0,1,1,1,0,1,0,1,0,1},
                         {1,0,0,0,0,0,0,0,0,0,1,9,1},
                         {1,1,1,1,1,1,1,1,1,1,1,1,1}};
       MazeRunner p = new MazeRunner(maze);
       p.runDFSAlgorithm(startingPoint[0],startingPoint[1]);
    }
}


And this is the class i use for my testings. It for sure works for this example but for greater arrays it doesn&#39;t. **Any suggestions would be appreciated**. When i run my program on big arrays i get this following error:

[![error messages][1]][1]


  [1]: https://i.stack.imgur.com/HTv5v.png

</details>


# 答案1
**得分**: 1

一般来说导致堆栈溢出异常的可能性只有两种
1. 堆栈内存不足
2. 存在一个死循环而且在使用递归时没有存在终止条件

由于您的算法在小尺寸迷宫上有效很可能是第一种情况正如您所知递归的规则是先进后出在Java虚拟机JVM所有未执行函数的数据将存储在堆栈中而堆栈拥有比堆更小的内存

<details>
<summary>英文:</summary>

Generally speaking there are only two possibilities that could cause a stackoverflow exception
1.memory of stack is not enough
2.there exist a dead loop which in using recursive means it does&#39;t exist an end condition.

Since you algo works for small size maze. Its possible the reason one. As you know the rule of recursive is first in last out, in JVM all the data of unexecuted functions will be stored in stack which owns a much smaller memory than heap.


</details>



# 答案2
**得分**: 1

你在实际工作中不应该使用递归算法除非你确定堆栈的深度会被限制在合理的范围内通常情况下这意味着O(log N)或者最多是O(log^2 N)

对于一个500x500的迷宫使用深度优先搜索DFS可能会在堆栈上产生大约25万次函数调用这太多了

如果你真的想使用DFS但你应该在一个单独的数据结构中维护自己的堆栈不过广度优先搜索BFS可能会更好除非你真的不希望得到最短路径

<details>
<summary>英文:</summary>

You should never use a recursive algorithm for real work unless you know for certain that the depth of the stack will be limited to a reasonable number.  Usually that means O(log N), or O(log^2 N) at most.

DFS for a 500x500 maze could put around 250000 function calls on the stack, which is way too many.

You can use DFS if you really want to, but you should maintain your own stack in a separate data structure.  BFS would be better though, unless there&#39;s some reason you really don&#39;t want the shortest path.

</details>



huangapple
  • 本文由 发表于 2020年10月9日 02:37:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/64268721.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定