1. 程式人生 > >Java與算法之(5) - 老鼠走迷宮(深度優先算法)

Java與算法之(5) - 老鼠走迷宮(深度優先算法)

tail 數字化 boa pop ase lis ext oar tar

小老鼠走進了格子迷宮,如何能繞過貓並以最短的路線吃到奶酪呢?

註意只能上下左右移動,不能斜著移動。

技術分享

在解決迷宮問題上,深度優先算法的思路是沿著一條路一直走,遇到障礙或走出邊界再返回嘗試別的路徑。

首先用一個二維數組來把迷宮“數字化”。

技術分享

[java] view plain copy print?技術分享技術分享
  1. int[][] maze = new int[5][4];

迷宮中每個格子的橫縱坐標對應數組的一維和二維索引,例如最左上角的格子是maze[0][0],數組的值表示該格子是否可以通過,0表示可以通過,1表示該格子有貓。

初始化迷宮,標記貓的位置:

[java] view plain copy
print?技術分享技術分享
  1. this.maze[2][0] = 1;
  2. this.maze[1][2] = 1;
  3. this.maze[2][2] = 1;
  4. this.maze[3][2] = 1;

起點位置坐標是x=0,y=0,如果向右移動就是x=x+1,y=y,向下移動是x=x,y=y+1。我們預先規定每到一個格子都按照右、下、左、上的順序嘗試下一個格子是否能走,如果右邊的格子沒有貓且未出邊界,就移動到下一個格子,繼續按照右、下、左、上的順序嘗試;如果右邊的格子不能走則嘗試下面的格子。

下面這個二維數組用來遍歷嘗試四個方向的格子:

[java] view plain copy
print?技術分享技術分享
  1. int[][] next = new int[][] {
  2. {1, 0},
  3. {0, 1},
  4. {-1, 0},
  5. {0, -1}
  6. };

為了不走回頭路,我們還需要另外一個二維數組標記哪些格子是已走過的,如果已走過則不能回頭。

[java] view plain copy print?技術分享技術分享
  1. int[][] mark = new int[5][4];

用一個棧記錄路徑

[java] view plain copy print?技術分享技術分享
  1. LinkedList<Integer> map = new LinkedList<>();

走格子的思路是:

[java] view plain copy print?技術分享技術分享
  1. for(遍歷四個方向的格子) {
  2. if(格子超出邊界 或 格子有貓 或 格子已經走過) {
  3. continue;
  4. } else {
  5. 移動到格子
  6. 記錄當前格子已走過
  7. 記錄當前路徑
  8. for(以新格子為中心遍歷四個方向的格子) {
  9. ......
  10. }
  11. }
  12. }

但是我們並不知道要走多少步才能到達目標,也就不知道循環要嵌套多少層,但是可以看出每次新的遍歷循環開啟後,執行的代碼和上一層循環是一樣的,所以這裏用遞歸解決。來看完整的代碼:

[java] view plain copy print?技術分享技術分享
  1. import java.util.LinkedList;
  2. public class DfsRatMaze {
  3. int min = Integer.MAX_VALUE;
  4. int endX = 3; //目標點橫坐標
  5. int endY = 3; //目標點縱坐標
  6. int width = 5; //迷宮寬度
  7. int height = 4; //迷宮高度
  8. int[][] maze = new int[5][4];
  9. int[][] mark = new int[5][4];
  10. LinkedList<Integer> map = new LinkedList<>();
  11. public void dfs(int startX, int startY, int step) {
  12. int[][] next = new int[][] { //按右->下->左->上的順序嘗試
  13. {1, 0},
  14. {0, 1},
  15. {-1, 0},
  16. {0, -1}
  17. };
  18. int nextX, nextY;
  19. int posible;
  20. if(startX == endX && startY == endY) {
  21. if(step < min)
  22. min = step;
  23. for(int i = map.size() - 1; i >= 0; i -= 2){
  24. nextX = map.get(i);
  25. nextY = map.get(i - 1);
  26. System.out.print("[" + nextX + "," + nextY + "]");
  27. if(i != 1)
  28. System.out.print("->");
  29. }
  30. System.out.println();
  31. return;
  32. }
  33. for(posible = 0; posible < next.length; posible++) { //按右->下->左->上的順序嘗試
  34. nextX = startX + next[posible][0];
  35. nextY = startY + next[posible][1];
  36. if(nextX < 0 || nextX >= width || nextY < 0 || nextY >= height) { //超出邊界
  37. continue;
  38. }
  39. if(maze[nextX][nextY] == 0 && mark[nextX][nextY] == 0) { //非障礙且未標記走過
  40. map.push(nextX);
  41. map.push(nextY);
  42. mark[nextX][nextY] = 1;
  43. dfs(nextX, nextY, step + 1); //遞歸調用, 移動到下一格
  44. mark[nextX][nextY] = 0;
  45. map.pop();
  46. map.pop();
  47. }
  48. }
  49. }
  50. /*
  51. * 初始化迷宮
  52. */
  53. public void initMaze() {
  54. this.maze = new int[width][height];
  55. this.mark = new int[width][height];
  56. this.maze[2][0] = 1;
  57. this.maze[1][2] = 1;
  58. this.maze[2][2] = 1;
  59. this.maze[3][2] = 1;
  60. this.mark[0][0] = 1;
  61. //打印迷宮 _表示可通行 *表示障礙 !表示目標
  62. for(int y = 0; y < height; y++) {
  63. for(int x = 0; x < width; x++) {
  64. if(x == endX && y == endY) {
  65. System.out.print("! ");
  66. } else if(this.maze[x][y] == 1) {
  67. System.out.print("* ");
  68. } else {
  69. System.out.print("_ ");
  70. }
  71. }
  72. System.out.println();
  73. }
  74. System.out.println();
  75. }
  76. public static void main(String[] args) {
  77. int startX = 0;
  78. int startY = 0;
  79. DfsRatMaze d = new DfsRatMaze();
  80. d.initMaze();
  81. d.dfs(startX, startY, 0);
  82. if(d.min < Integer.MAX_VALUE)
  83. System.out.println("最少需要" + d.min + "步");
  84. else
  85. System.out.println("目標地點無法到達");
  86. }
  87. }

運行後輸出:

[java] view plain copy print?技術分享技術分享
  1. [1,0]->[1,1]->[2,1]->[3,1]->[4,1]->[4,2]->[4,3]->[3,3]
  2. [1,0]->[1,1]->[2,1]->[3,1]->[3,0]->[4,0]->[4,1]->[4,2]->[4,3]->[3,3]
  3. [1,0]->[1,1]->[0,1]->[0,2]->[0,3]->[1,3]->[2,3]->[3,3]
  4. [0,1]->[1,1]->[2,1]->[3,1]->[4,1]->[4,2]->[4,3]->[3,3]
  5. [0,1]->[1,1]->[2,1]->[3,1]->[3,0]->[4,0]->[4,1]->[4,2]->[4,3]->[3,3]
  6. [0,1]->[0,2]->[0,3]->[1,3]->[2,3]->[3,3]
  7. 最少需要6步

可以看到,程序計算出了所有路線,並找到了最短的路線。而整個代碼還不到100行,真是神奇的算法。

Java與算法之(5) - 老鼠走迷宮(深度優先算法)