前言
搜尋界兩大基礎搜尋演算法分別是廣度優先搜尋和深度優先搜尋。
搜尋演算法可以用於尋找圖的連通性。
一個普通的二維地圖,從A點到B點,有兩個問題。 能否到達。 怎樣以最短的路徑走到B點。
這是搜尋演算法能夠解決的事情。
要使用廣度優先搜尋演算法來搜尋從A點到B點的最短路徑
依據廣度優先的原則,每走一步,都將下一步所有可能的選擇入隊之後,才進行下一步
結束標記:
隊列為空白時,搜尋結束
搜尋到指定結果時,搜尋結束 實現功能
在一個二維地圖中 計算A點到B點的最少步數 輸出最少步數的路徑 中文版參考實現
* * A * B C * D G E * F * 搜尋從A到F的最少步數 * 寬度優先搜尋: * 第一步: * A入隊,步數是0 * A為隊列頭 * 第二步: * 查看A下一步的所有選擇 * 有B和C,所以第二步可以到達B和C,步數是1 * B、C入隊,同時標記B和C,表示已經走過了。 * A已經沒用了,因為A所有可能到達的點已經在隊列裡了,所以A出隊。 * 判斷是否達到目標地點,是,結束迴圈。當前步數是最短步數 * 否則繼續 * 此時隊列頭為B * 第三步: * B、C是同時入隊的,所以兩個都要在第三步裡擴充所有可能。 * * 1. 查看B下一步的所有選擇 * 有D、G * D、G入隊,標記D、G * B也沒用了,B出隊 * 判斷是否達到目標地點,是,結束迴圈。當前步數是最短步數 * 否則繼續 * 此時隊列頭為C * 2. 查看C下一步的所有選擇 * 有G、E,但是G已經標記走過了 * 所以只有E可選 * E入隊,同時標記E * C已無效,C出隊 * 此時隊列頭為D * 合并B、C擴充出來的所有選擇,有D、G、E * 所以第三步可以達到的是D、G、E,步數是2 * 判斷是否達到目標地點,是,結束迴圈。當前步數是最短步數 * 否則繼續 * 第四步: * 搜尋D、G、E所有的的擴充可能 * * 1. 查看D下一步的所有選擇 * 有F,F是目標,F入隊,此時步數是3 * D出隊 * 判斷是否達到目標地點,是,結束迴圈。當前步數是最短步數 * 發現F是目標,結束遍曆。 * * A到達F的最小步數是3 */
代碼實現
/** * 一個5行4列的地圖 * 其中部分地方有障礙無法通過 * 找出從(1, 1)到(4, 3)的最少步數是多少 * 輸出路徑 */public class WalkingLabyrinth { static int maxLine = 6; static int maxRow = 5; static int[][] map = new int[maxLine][maxRow]; static int[][] mark = new int[maxLine][maxRow]; static int targetLine = 4; static int targetRow = 3; static int step = -1; static int[][] nextDirection = { {0, -1}, {1, 0}, {0, 1}, {-1, 0} }; public static void main(String[] args) { map[1][3] = 1; map[3][3] = 1; map[4][2] = 1; map[5][4] = 1; //堵住所有道路 //map[4][4] = 1; //map[5][3] = 1; bfs(1, 1); System.out.print(step == -1 ? "\n沒有到達" : "\n到達"); System.out.println("目標位置: " + targetLine + "行" + targetRow + "列,步數:" + step); } public static void bfs(int startLine, int startRow) { mark[startLine][startRow] = 1; Node[] queue = new Node[maxLine * maxRow]; int head = 0; int tail = 0; Node tNode = new Node(startLine, startRow, 0, head); queue[tail++] = tNode; int tLine, tRow; while (head < tail) { for (int i = 0; i < 4; i++) { tLine = queue[head].getLine() + nextDirection[i][0]; tRow = queue[head].getRow() + nextDirection[i][1]; if (tLine >= maxLine || tRow >= maxRow || tLine < 1 || tRow < 1) { continue; } if (mark[tLine][tRow] != 1 && map[tLine][tRow] != 1) { tNode = new Node(tRow, tLine, queue[head].getStep() + 1, head); queue[tail++] = tNode; //System.out.println("當前位置: " + tLine + "行" + tRow + "列,步數:" + queue[tail - 1].getStep()); mark[tLine][tRow] = 1; } if (tLine == targetLine && tRow == targetRow) { step = queue[tail - 1].getStep(); System.out.println("經過路徑:"); int index = tail - 1; for (int j = 0; j <= step; j++) { System.out.println("("+ queue[index].getLine() + "," + queue[index].getRow() +")"); index = queue[index].getP(); } return; } } head++; } step = -1; } private static class Node { int row, line, step; int p; public int getP() { return p; } Node(int line, int row) { this.line = line; this.row = row; } Node(int row, int line, int step, int p) { this.row = row; this.line = line; this.step = step; this.p = p; } public int getRow() { return row; } public int getLine() { return line; } public int getStep() { return step; } }}
結果
經過路徑:(4,3)(5,3)(5,2)(5,1)(4,1)(3,1)(2,1)(1,1)到達目標位置: 4行3列,步數:7
一點收穫
原來簡單類比隊列,可以簡單到如此程度。。
一個數組,兩個索引。
int index = tail - 1; for (int j = 0; j <= step; j++) { System.out.println("("+ queue[index].getLine() + "," + queue[index].getRow() +")"); index = queue[index].getP(); }
感覺我的這段代碼充滿了智慧恩,這是傳說中的推倒(ノ*・ω・)ノ,好吧是反向推算。
之前是看過的,過了不到三個月,再看跟新的一樣。太久沒用了,還是得日久才能熟悉。沒事要瞄一眼代碼留點印象。 END