演算法(三):圖解廣度優先搜尋演算法

CodeInfo發表於2018-08-03

演算法簡介

廣度優先搜尋演算法(Breadth First Search),又稱為"寬度優先搜尋"或"橫向優先搜尋",簡稱BFS; BFS是用於的查詢演算法(要求能用圖表示出問題的關聯性)。

BFS可用於解決2類問題:

  • 從A出發是否存在到達B的路徑;
  • 從A出發到達B的最短路徑(這個應該叫最少步驟合理);

其思路為從圖上一個節點出發,訪問先訪問其直接相連的子節點,若子節點不符合,再問其子節點的子節點,按級別順序依次訪問,直到訪問到目標節點。

所謂的"圖"為:

演算法(三):圖解廣度優先搜尋演算法

案例

演算法(三):圖解廣度優先搜尋演算法

如上圖所示,找出從A到H的最短路徑(步驟最少的,假設每一段距離相等),此時就可以使用廣域搜尋演算法,原理步驟為:

  1. 假設存在一個空的搜尋佇列Queue,首先將節點A新增進佇列Queue
  2. 判斷佇列第一個節點是否是需要查詢的目標節點,若不是,則將第一個節點的直接子節點新增進佇列,並移除第一個節點
  3. 重複判斷,直到第一個節點為目標節點,或者佇列為空(即代表沒有合適的)

如下圖所示:

演算法(三):圖解廣度優先搜尋演算法

過濾已經搜尋過的節點

演算法(三):圖解廣度優先搜尋演算法

對於已經搜尋過的節點,最好將其唯一的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
複製程式碼

掃描關注微信公眾號:

演算法(三):圖解廣度優先搜尋演算法

相關文章