簡體   English   中英

深度優先搜索遞歸調用導致 StackOverflow 錯誤

[英]Depth First Search recursive calls cause StackOverflow error

我正在使用深度優先搜索算法的實現來解決迷宮問題。 我不希望找到最短路徑,而是希望找到找到有效路徑的最快方法。 這是我用來解決迷宮的方法。 迷宮是 2d int arrays,其中 0 是一個開放的方塊,1 是一堵牆,2 是一個訪問過的方塊,9 是目的地。

    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<Integer> 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;
}

}

我的算法適用於小型迷宮。 當我想解決一個 50 * 50 的迷宮時,它非常快。 當我移動到 500 * 500 時,出現堆棧溢出錯誤。 我可以理解它是由於 function 的許多遞歸調用而出現的,但我不知道如何修復它。 有沒有另一種方法,這樣我仍然可以使用深度優先搜索並存儲我的路徑但沒有堆棧溢出? 或者是否可以在我的代碼中進行任何更改以修復它?

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<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("Time for Depth First Algorithm: "+((double) (stopTime-startTime) / 1_000_000)+" milliseconds");

}

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("From ("+fromY+", "+fromX+") to ("+toY+", "+toX+")");
            bool++;
            continue;
        }
        if (bool == 1){
            fromX = toX;
            fromY = toY;
            toX = (int) li.previous();
            toY = (int) li.previous();
            System.out.println("From ("+fromY+", "+fromX+") to ("+toY+", "+toX+")");
            i++;
        }
    }
    System.out.println("\nLength of path is : "+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]);
}

}

這是我用於測試的 class。 它肯定適用於此示例,但對於更大的 arrays 它不起作用。 任何建議將不勝感激 當我在大 arrays 上運行我的程序時,出現以下錯誤:

錯誤信息

一般來說,只有兩種可能導致堆棧溢出異常 1.memory 堆棧不夠 2.存在死循環,在使用遞歸時意味着它不存在結束條件。

由於您的算法適用於小型迷宮。 這可能是一個原因。 眾所周知遞歸的規則是先進后出,在 JVM 中所有未執行函數的數據將存儲在堆棧中,堆棧擁有比堆小得多的 memory 。

除非您確定堆棧的深度將被限制在一個合理的數字,否則您永遠不應該在實際工作中使用遞歸算法。 通常這意味着 O(log N),或最多 O(log^2 N)。

500x500 迷宮的 DFS 可以在堆棧上放置大約 250000 個 function 調用,這太多了。

如果你真的願意,你可以使用 DFS,但你應該在一個單獨的數據結構中維護你自己的堆棧。 不過,BFS 會更好,除非出於某種原因您真的不想要最短路徑。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM