您好,登錄后才能下訂單哦!
本文小編為大家詳細介紹“怎么用Java深度優先遍歷解決迷宮問題”,內容詳細,步驟清晰,細節處理妥當,希望這篇“怎么用Java深度優先遍歷解決迷宮問題”文章能幫助大家解決疑惑,下面跟著小編的思路慢慢深入,一起來學習新知識吧。
什么是深度,即向下,深度優先,即向下優先,一口氣走到底,走到底發現沒路再往回走。
在算法實現上來講,深度優先可以考慮是遞歸的代名詞,深度優先搜索必然需要使用到遞歸的思路。
有的人可能會說了,我可以用棧來實現,以迭代的方式,那么問題來了,棧這種數據結構,同學們認為是否也囊括了遞歸呢?Java語言的方法區本身也是實現在一個棧空間上的。
我們以一個簡單的迷宮為例,以1代表墻,0代表路徑,我們構造一個具有出入口的迷宮。
1 1 0 1 1 1 1 1 1
1 0 0 0 0 0 0 1 1
1 0 1 1 1 1 0 1 1
1 0 0 0 0 1 0 0 1
1 1 1 1 1 1 1 0 1
以上面這個0為入口,下面這個0為出口,那么深度優先的算法遍歷順序,方向的遍歷順序為左下右上,以dp[0][2]為入口,我把這個過程列在下面了:
第一步:
dp[0][2] -> dp[1][2]
第二步:
dp[1][2] -> dp[1][1]
第三步:
dp[1][1] -> dp[2][1]
第四步:
dp[2][1] -> dp[3][1]
第五步:
dp[3][1] -> dp[3][2]
第六步:
dp[3][2] -> dp[3][3]
第七步:
dp[3][3] -> dp[3][4]
第八步:
dp[3][4] -> dp[3][5] 由于 dp[3][5]是墻,所以深度優先算法需要開始回退,最終會回退到dp[1][2]這個位置,然后向右走
第八步:
dp[1][2] -> dp[1][3]
第九步:
dp[1][3] -> dp[1][4]
第十步:
dp[1][4] -> dp[1][5]
第十一步:
dp[1][5] -> dp[1][6]
第十二步:
dp[1][6] -> dp[2][6]
第十三步:
dp[2][6] -> dp[3][6]
第十四步:
dp[3][6] -> dp[3][7]
第十五步:
dp[3][7] -> dp[4][7] 終點,程序退出
可以發現,深度優先算法有點像我們的人生,需要不斷試錯,錯了就退,直到找到一條通往出口的路。
現在讓我們動手用代碼實現一下上面的步驟吧。
以深度優先的方式解決這個問題,主要考慮兩點,首先是如何擴展節點,我們的順序是左,下,右,上,那么,應該以什么樣的方式實現這個呢?第二點,就是如何實現深度優先,雖然原理上肯定是遞歸,但是應該如何遞歸呢?要解決這兩個問題,請看示例代碼,以Java為例:
package com.chaojilaji.book; import com.chaojilaji.book.util.InputUtils; import java.util.HashSet; import java.util.Set; import static com.chaojilaji.book.util.CheckUtils.canAdd; public class Dfs { public static Integer dfs(String[][] a, int currentX, int currentY, int chux, int chuy, Set<Integer> cache) { System.out.println(currentY + " " + currentX); if (currentX == chux && currentY == chuy) { return 1; } // TODO: 2022/1/11 枚舉子節點,左 下 右 上 int[] x = new int[]{-1, 0, 1, 0}; int[] y = new int[]{0, 1, 0, -1}; for (int i = 0; i < 4; i++) { if (canAdd(a, currentX + x[i], currentY + y[i], cache)) { Integer tmp = dfs(a, currentX + x[i], currentY + y[i], chux, chuy, cache); if (tmp != 0) { System.out.println(currentY + " " + currentX + " 結果路徑"); return tmp + 1; } } } System.out.println(currentY + " " + currentX + " 回滾"); return 0; } public static Integer getAns(String[][] a) { int m = a[0].length; int n = a.length; int rux = -1, ruy = 0; int chux = -1, chuy = n - 1; for (int i = 0; i < m; i++) { if (a[0][i].equals("0")) { // TODO: 2022/1/11 找到入口 rux = i; } if (a[n - 1][i].equals("0")) { chux = i; } } Set<Integer> cache = new HashSet<>(); cache.add(rux * 100000 + ruy); System.out.println("打印行走過程"); return dfs(a, rux, ruy, chux, chuy, cache)-1; } public static void demo() { String x = "1 1 0 1 1 1 1 1 1\n" + "1 0 0 0 0 0 0 1 1\n" + "1 0 1 1 1 1 0 1 1\n" + "1 0 0 0 0 1 0 0 1\n" + "1 1 1 1 1 1 1 0 1"; String[][] a = InputUtils.getInput(x); Integer ans = getAns(a); System.out.println(ans == -1 ? "不可達" : "可達,需要行走" + ans + "步"); } public static void main(String[] args) { demo(); } }
這里的canAdd方法是臨界判斷函數,如下:
/** * 臨界判斷 * @param a * @param x * @param y * @param cache * @return */ public static Boolean canAdd(String[][] a, Integer x, Integer y, Set<Integer> cache) { int m = a[0].length; int n = a.length; if (x < 0 || x >= m) { return false; } if (y < 0 || y >= n) { return false; } if (a[y][x].equals("0") && !cache.contains(x * 100000 + y)) { cache.add(x * 100000 + y); return true; } return false; }
可以瞧見,這里面最核心的代碼在于dfs這個函數,讓我們來深入分析一波
public static Integer dfs(String[][] a, int currentX, int currentY, int chux, int chuy, Set<Integer> cache) { System.out.println(currentY + " " + currentX); if (currentX == chux && currentY == chuy) { return 1; } // TODO: 2022/1/11 枚舉子節點,左 下 右 上 int[] x = new int[]{-1, 0, 1, 0}; int[] y = new int[]{0, 1, 0, -1}; for (int i = 0; i < 4; i++) { if (canAdd(a, currentX + x[i], currentY + y[i], cache)) { Integer tmp = dfs(a, currentX + x[i], currentY + y[i], chux, chuy, cache); if (tmp != 0) { System.out.println(currentY + " " + currentX + " 結果路徑"); return tmp + 1; } } } System.out.println(currentY + " " + currentX + " 回滾"); return 0; }
首先,dfs深度優先,首先應該寫的是判斷終止條件,這里的終止條件就是到達終點,即目前的橫縱坐標等于出口的橫縱坐標。
然后,我們利用兩個方向數組作為移動方案,也就是
// TODO: 2022/1/11 枚舉子節點,左 下 右 上 int[] x = new int[]{-1, 0, 1, 0}; int[] y = new int[]{0, 1, 0, -1}; for (int i = 0; i < 4; i++) { if (canAdd(a, currentX + x[i], currentY + y[i], cache)) { } }
這種方法,是數組類型的移動方式的兼容寫法,不管你的移動方向有多少,都可以配在x和y兩個數組中。定義了四個方向,現在我們需要思考遞歸的過程。
既然我完成的時候是返回1,那么其實如果在這條路上的所有都應該加1,所以,就有了下面的判斷
if (canAdd(a, currentX + x[i], currentY + y[i], cache)) { Integer tmp = dfs(a, currentX + x[i], currentY + y[i], chux, chuy, cache); if (tmp != 0) { System.out.println(currentY + " " + currentX + " 結果路徑"); return tmp + 1; } }
當子dfs出來的結果不為0,說明該子dfs是可以到達出口的,那么直接把結果加1返回給上層即可。如果子dfs出來的結果為0,說明該子dfs是不能到達出口的,就直接返回0即可。
讀到這里,這篇“怎么用Java深度優先遍歷解決迷宮問題”文章已經介紹完畢,想要掌握這篇文章的知識點還需要大家自己動手實踐使用過才能領會,如果想了解更多相關內容的文章,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。