有一個數組訪問越界的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; } } } } }