Java 遞歸(深度優先)尋找迷宮最短路徑


有一個數組訪問越界的bug,通過連續的遞歸躲過了一開始的邊界檢查,記錄一下。

public class Maze {
    public static void main(String[] args) {
        


        //創建迷宮,0未走可走,1未走不可走(障礙物),
        //         2走過可走,3走過不可走
        int[][] map = new int[9][8];
        int[][] lastPath = new int[9][8];
        int[][] thisPath = new int[9][8];
        int lastDis;
        for (int i = 0; i < map[0].length; i++) {
            map[0][i] = 1;
            map[8][i] = 1;
        }
        for (int i = 0; i < map.length; i++) {
            map[i][0] = 1;
            map[i][7] = 1;
        }

        //設置障礙物
        map[4][1] = 1;
        map[4][2] = 1;
        map[4][3] = 1;
        map[2][3] = 1;
        map[2][2] = 1;
        map[3][3] = 1;
        map[3][5] = 1;
        map[6][3] = 1;
        map[7][5] = 1;



        T t1 = new T();
        //走前迷宮
        t1.printMaze(map);

        //找路
        lastPath[0][0] = 8 * 7;
        t1.findMinWay(map, 1, 1, lastPath, 0);

        //走后迷宮
        t1.printMaze(lastPath);

    }
}

class T {

    public void printMaze(int[][] map) {
        System.out.println("打印迷宮如下============");
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[i].length; j++) {
                System.out.print(map[i][j] + " ");
            }
            System.out.println();
        }
    }

    //復制當前的最短路徑
    public void refreshPath(int[][] map, int[][] lastPath, int nowDepth) {
        
        //先清空原有路徑
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[i].length; j++) {
                lastPath[i][j] = 0;
            }
        }

        //再復制新的最短路徑
        lastPath[0][0] = nowDepth;
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[i].length; j++) {
                if (map[i][j] == 2) {
                    lastPath[i][j] = map[i][j];
                }
            }
        }
    }

    //判斷從當前位置map[i][j]是否能找到路到達終點的路
    //depth表示遞歸深度(路的長度)
    public void findMinWay(int[][] map, int i, int j, int[][] lastPath, int nowDepth) {

        //比較nowDepth和lastPath[0][0]的大小,如果目前走的距離已經比最短路徑長,則返回false
        //但是,不將當前位置置為2,而是不做處理,只是剪掉了當前路徑這種選擇
        if (nowDepth >= lastPath[0][0]) {
            System.out.println("剪枝" + nowDepth + ">=" + lastPath[0][0]);                                    
            return;
        } else {//繼續嘗試
            if (map[7][6] != 1 && i == 7 && j == 6) {//已經找到出口,並且這條路比之前的最短路徑都短,則記錄路徑,並更新最短距離
                lastPath[0][0] = nowDepth;
                refreshPath(map, lastPath, nowDepth);
                System.out.println("找到新的最短路徑" + lastPath[0][0]);
                return;
            } else {//還在找路
                System.out.printf("(%d, %d)\n", i, j);

                if (map[i][j] == 0) {//當前路不是障礙物,可以嘗試走 //先假定當前位置可以走通 map[i][j] = 2; //System.out.printf("(%d, %d) %d\n", i, j, nowDepth); //再尋找下一個位置,遞歸到目標 findMinWay(map, i + 1, j, lastPath, nowDepth + 1); findMinWay(map, i, j + 1, lastPath, nowDepth + 1); findMinWay(map, i - 1, j, lastPath, nowDepth + 1); findMinWay(map, i, j - 1, lastPath, nowDepth + 1); map[i][j] = 0; }

            }
        }
    }
}

重點是上面的部分,數組訪問越界出現在四周沒有路,需要回退的時候,回退一步,就可以獲得一次越界的機會,雖然邊界用1表示障礙物,做了一層防護,但是當回退兩步的時候,還是會出現數組訪問越界的情況。

 

 

修改后,如下

public class Maze {
    public static void main(String[] args) {
        


        //創建迷宮,0未走可走,1未走不可走(障礙物),
        //         2走過可走(當前路徑中包含該位置)
        int[][] map = new int[9][8];

        //記錄最短路徑和最短距離
        //因為還沒學全局變量,所以最短距離記錄在lastPath[0][0]中
        int[][] lastPath = new int[9][8];

        //數組的外圍相當於迷宮的圍牆,不允許走,初步防止數組訪問越界
        for (int i = 0; i < map[0].length; i++) {
            map[0][i] = 1;
            map[8][i] = 1;
        }
        for (int i = 0; i < map.length; i++) {
            map[i][0] = 1;
            map[i][7] = 1;
        }

        //設置障礙物
        map[4][1] = 1;
        map[4][2] = 1;
        map[4][3] = 1;
        map[2][3] = 1;
        map[2][2] = 1;
        map[3][3] = 1;
        map[3][5] = 1;
        map[6][3] = 1;
        map[7][5] = 1;



        T t1 = new T();

        //打印走前迷宮
        t1.printMaze(map);

        //找最短路徑
        //初始化最短距離為數組全部元素的數量
        lastPath[0][0] = 8 * 7;
        t1.findMinWay(map, 1, 1, lastPath, 0);

        //打印最短路徑
        t1.printMinPath(lastPath);



    }
}

class T {

    public void printMaze(int[][] map) {
        System.out.println("打印迷宮如下============");
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[i].length; j++) {
                System.out.print(map[i][j] + " ");
            }
            System.out.println();
        }
    }

    //復制當前的最短路徑
    public void refreshPath(int[][] map, int[][] lastPath, int nowDepth) {
        
        //先清空原有路徑
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[i].length; j++) {
                lastPath[i][j] = 0;
            }
        }

        //記錄新的最短距離
        lastPath[0][0] = nowDepth;

        //再復制新的最短路徑
        for (int i = 0; i < map.length; i++) {
            for (int j = 0; j < map[i].length; j++) {
                if (map[i][j] == 2) {
                    lastPath[i][j] = map[i][j];
                }
            }
        }
    }

    public void printMinPath(int[][] lastPath) {
        System.out.println("最短路徑如下============");
        for (int i = 0; i < lastPath.length; i++) {
            for (int j = 0; j < lastPath[i].length; j++) {
                System.out.print(lastPath[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println("最短距離為" + lastPath[0][0]);
    }

    //判斷map[i][j]是否在數組內,防止數組訪問越界
    public boolean inArr(int[][] map, int i, int j) {
        return (i >= 0 && i < map.length && j >= 0 && j < map[i].length); //map[i]用到了邏輯與的短路效應,只有i未越界,后面的判斷才會繼續
    }

    //尋找從當前位置map[i][j]到達終點的最短路徑和最短距離
    //nowDepth表示遞歸深度(路的長度)
    public void findMinWay(int[][] map, int i, int j, int[][] lastPath, int nowDepth) {
        
        //比較nowDepth和lastPath[0][0]的大小,如果目前走的距離已經比最短路徑長,則直接返回。剪枝,剪掉了當前路徑這種選擇
        //但是,不將當前位置置為2,而是不做處理
        if (nowDepth >= lastPath[0][0]) {                    
            return;
        } else {//繼續嘗試
            if (map[7][6] == 0 && i == 7 && j == 6) {//已經找到出口,並且這條路比之前的最短路徑都短,則記錄路徑,並更新最短距離
                lastPath[0][0] = nowDepth;
                map[7][6] = 2;
                refreshPath(map, lastPath, nowDepth);
                map[7][6] = 0;//先令終點為2,記錄路徑。然后置0,表示未走可走,這里也是回退一步的意思。
                return;
            } else {//還在找路
                

                if (map[i][j] == 0) {//當前路不是障礙物,可以嘗試走
                    //先假定當前位置可以走通
                    map[i][j] = 2;
                    
                    //再尋找下一個位置,遞歸到目標
                    if (inArr(map, i + 1, j))//進行邊界判斷
                        findMinWay(map, i + 1, j, lastPath, nowDepth + 1);

                    if (inArr(map, i, j + 1))
                        findMinWay(map, i, j + 1, lastPath, nowDepth + 1);

                    if (inArr(map, i - 1, j))
                        findMinWay(map, i - 1, j, lastPath, nowDepth + 1);

                    if (inArr(map, i, j - 1))
                        findMinWay(map, i, j - 1, lastPath, nowDepth + 1);

                    map[i][j] = 0;
                    
                }

            }
        }
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM