演算法簡介
廣度優先搜尋演算法(Breadth First Search),又稱為"寬度優先搜尋"或"橫向優先搜尋",簡稱BFS; BFS是用於圖的查詢演算法(要求能用圖表示出問題的關聯性)。
BFS可用於解決2類問題:
- 從A出發是否存在到達B的路徑;
- 從A出發到達B的最短路徑(這個應該叫最少步驟合理);
其思路為從圖上一個節點出發,訪問先訪問其直接相連的子節點,若子節點不符合,再問其子節點的子節點,按級別順序依次訪問,直到訪問到目標節點。
所謂的"圖"為:
案例
如上圖所示,找出從A到H的最短路徑(步驟最少的,假設每一段距離相等),此時就可以使用廣域搜尋演算法,原理步驟為:
- 假設存在一個空的搜尋佇列Queue,首先將節點A新增進佇列Queue
- 判斷佇列第一個節點是否是需要查詢的目標節點,若不是,則將第一個節點的直接子節點新增進佇列,並移除第一個節點
- 重複判斷,直到第一個節點為目標節點,或者佇列為空(即代表沒有合適的)
如下圖所示:
過濾已經搜尋過的節點
對於已經搜尋過的節點,最好將其唯一的id標識儲存下來,後續搜尋過程中如果再次出現該節點則跳過不再重複搜尋,以提高效率和避免死迴圈;
java實現
public class BFS {
public static void main(String[] args){
//初始化先建立起各個節點資訊,以及對應的直接子節點列表
HashMap<String,String[]> map = new HashMap<>();
map.put("A", new String[] {"B","C"});
map.put("B", new String[] {"E"});
map.put("C", new String[] {"D","F"});
map.put("D", new String[] {"E"});
map.put("E", new String[] {"H"});
map.put("F", new String[] {"E","G"});
map.put("G", new String[] {"H"});
map.put("H", new String[] {});
//獲取從A到H的最短路徑節點連結串列
Node target = findTarget("A","H",map);
//列印出最短路徑的各個節點資訊
printSearPath(target);
}
/**
* 列印出到達節點target所經過的各個節點資訊
* @param target
*/
static void printSearPath(Node target) {
if (target != null) {
System.out.print("找到了目標節點:" + target.id + "\n");
List<Node> searchPath = new ArrayList<Node>();
searchPath.add(target);
Node node = target.parent;
while(node!=null) {
searchPath.add(node);
node = node.parent;
}
String path = "";
for(int i=searchPath.size()-1;i>=0;i--) {
path += searchPath.get(i).id;
if(i!=0) {
path += "-->";
}
}
System.out.print("步數最短:"+path);
} else {
System.out.print("未找到了目標節點");
}
}
/**
* 從指定的開始節點 startId ,查詢到目標節點targetId 的最短路徑
* @param startId
* @param targetId
* @param map
* @return
*/
static Node findTarget(String startId,String targetId,HashMap<String,String[]> map) {
List<String> hasSearchList = new ArrayList<String>();
LinkedList<Node> queue = new LinkedList<Node>();
queue.offer(new Node(startId,null));
while(!queue.isEmpty()) {
Node node = queue.poll();
if(hasSearchList.contains(node.id)) {
//跳過已經搜尋過的,避免重複或者死迴圈
continue;
}
System.out.print("判斷節點:" + node.id +"\n");
if (targetId.equals(node.id)) {
return node;
}
hasSearchList.add(node.id);
if (map.get(node.id) != null && map.get(node.id).length > 0) {
for (String childId : map.get(node.id)) {
queue.offer(new Node(childId,node));
}
}
}
return null;
}
/**
* 節點物件
* @author Administrator
*
*/
static class Node{
//節點唯一id
public String id;
//該節點的直接父節點
public Node parent;
//該節點的直接子節點
public List<Node> childs = new ArrayList<Node>();
public Node(String id,Node parent) {
this.id = id;
this.parent = parent;
}
}
}
複製程式碼
執行完main方法列印資訊如下:
判斷節點:A
判斷節點:B
判斷節點:C
判斷節點:E
判斷節點:D
判斷節點:F
判斷節點:H
找到了目標節點:H
步數最短:A-->B-->E-->H
複製程式碼