廣度優先搜索(BFS)總結及其Java實現

圖是搜索算法的最常用數據結構,因此需要先儲備一些圖的概念。
我總結在另一篇博客:非線性數據結構——圖的相關概念
BFS 相對 DFS 的最主要的區別是:BFS 找到的路徑一定是最短的,但代價就是空間複雜度比 DFS 大很多

廣度優先搜索(Breadth-First-Search)

廣度優先的思路比較簡單,寫 BFS 算法都是用「隊列」這種數據結構,每次將一個節點周圍的所有節點加入隊列。可以通過下面這張圖直接理解:
在這裏插入圖片描述
1.BFS算法思路簡述
設置一個數組visited[]存放每個結點是否被訪問過
(1)訪問第一個頂點,置已訪問標誌;
(2)取第一個鄰接點;
(3)若未被訪問過,則對其進行DFS;
(4)取下一個鄰接點;
重複(3)、(4)直至所有鄰接頂點取完。
BFS是一種分層的搜索過程(典型:樹的層次遍歷) ,每向前走一步可能訪問一批頂點, 不像深度優先搜索那樣有往回退的情況。因此, 廣度優先搜索不是一個遞歸的過程。
2.圖的代碼實現

public class Graph{ //無向圖
   private int v;   //頂點的個數
   private LinkedList<Integer> adj[];   //鄰接表
   public Graph(int v){
        this.v=v;    adj = new LinkedList[v];
        for(int i=0;i<v;i++){  
        adj[i] = new LinkedList<>();  
        }
   }   //無向圖一條邊存兩次
   public void addEdge(int s, int t){
       adj[s].add(t);
       adj[t].add(s);
   }
}

3.BFS代碼實現
搜索一條從 s 到 t 的最短路徑:
隊列的作用:因爲廣度優先搜索是逐層訪問的,當我們訪問到第 k 層的頂點的時候,我們需要把第 k 層的頂點記錄下來,稍後才能通過第 k 層的頂點來找第 k+1 層的頂點。所以,我們用這個隊列來實現記錄的功能。
從頂點 s 開始,廣度優先搜索到頂點 t 後,prev 數組中存儲的就是搜索的路徑。不過這個路徑是反向存儲的。比如,通過頂點 2 的鄰接表訪問到頂點 3,那 prev[3]就等於 2。

public void bfs(int s, int t) {
  if (s == t) return;
  //visited 是用來記錄已經被訪問的頂點,用來避免頂點被重複訪問。
  boolean[] visited = new boolean[v];
  visited[s]=true;
  //queue 用來存儲本身已被訪問、但相連的頂點還沒有被訪問的頂點。
  Queue<Integer> queue = new LinkedList<>();
  queue.add(s);
  //prev 用來記錄搜索路徑。
  int[] prev = new int[v];
  for (int i = 0; i < v; ++i) {   prev[i] = -1;  }
  //
  while (queue.size() != 0) {
    int w = queue.poll();
    for (int i = 0; i < adj[w].size(); ++i) {
       int q = adj[w].get(i);
       if (!visited[q]) {
         prev[q] = w;
         if (q == t) { //搜索到t,直接返回prev數組,注意逆序
           print(prev, s, t);
           return;
        }
        visited[q] = true;//頂點 q 被訪問,visited[q]置爲 true。
        queue.add(q);
      }
    }
  }
}
//爲了正向打印出路徑,我們需要遞歸地來打印
private void print(int[] prev, int s, int t) { // 遞歸打印s->t的路徑
  if (prev[t] != -1 && t != s) {
    print(prev, s, prev[t]);
  }
  System.out.print(t + " ");
}

本文代碼代碼部分均來極客時間的《數據結構與算法之美》第31課

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章