廣度優先搜索


廣度優先搜索(BFS:Breadth-First Search)是一種圖搜索策略,其將搜索限制到 2 種操作:

  • (a) 訪問圖中的一個節點;
  • (b) 訪問該節點的鄰居節點;

廣度優先搜索(BFS)Edward F. Moore 在 1950 年發表,起初被用於在迷宮中尋找最短路徑。在 Prim 最小生成樹算法和 Dijkstra 單源最短路徑算法中,都采用了與廣度優先搜索類似的思想。

對圖的廣度優先搜索與對樹(Tree)的廣度優先遍歷(Breadth First Traversal)是類似的,區別在於圖中可能存在環,所以可能會遍歷到已經遍歷的節點。BFD 算法首先會發現和源頂點 s 距離邊數為 k 的所有頂點,然后才會發現和 s 距離邊數為 k+1 的其他頂點。

例如,下面的圖中,從頂點 2 開始遍歷,當遍歷到頂點 0 時,鄰接的頂點為 1 和 2,而頂點 2 已經遍歷過,如果不做標記,遍歷過程將陷入死循環。所以,在 BFS 的算法實現中需要對頂點是否訪問過做標記。

上圖的 BFS 遍歷結果為 [ 2, 0, 3, 1 ]。

BFS 算法的實現通常使用隊列(Queue)數據結構來存儲遍歷圖中節點的中間狀態,過程如下:

  1. 將 root 節點 Enqueue;
  2. Dequeue 一個節點,並檢查該節點:
    • 如果該節點就是要找的目標節點,則結束遍歷,返回結果 "Found";
    • 否則,Enqueue 所有直接后繼子節點(如果節點未被訪問過);
  3. 如果 Queue 為空,並且圖中的所有節點都被檢查過,仍未找到目標節點,則結束搜索,返回結果 "Not Found";
  4. 如果 Queue 不為空,重復步驟 2;

如果需要記錄搜索的軌跡,可以為頂點着色。起初所有頂點為白色,隨着搜索的進行變為灰色,然后變成黑色。灰色和黑色頂點都是已發現的頂點。

BFS 算法偽碼如下:

 1 procedure BFS(G,v) is
 2     create a queue Q
 3     create a set V
 4     add v to V
 5     enqueue v onto Q
 6     while Q is not empty loop
 7        t = Q.dequeue()
 8        if t is what we are looking for then
 9           return t
10        end if
11        for all edges e in G.adjacentEdges(t) loop
12           u = G.adjacentVertex(t,e)
13           if u is not in V then
14               add u to V
15               enqueue u onto Q
16           end if
17        end loop
18     end loop
19     return none
20 end BFS

廣度優先搜索(BFS)的時間復雜度為 O(V+E),V 即 Vertex 頂點數量,E 即 Edge 邊數量。

BFS 算法實現代碼如下:

  1 using System;
  2 using System.Collections.Generic;
  3 
  4 namespace GraphAlgorithmTesting
  5 {
  6   class Program
  7   {
  8     static void Main(string[] args)
  9     {
 10       Graph g = new Graph(4);
 11       g.AddEdge(0, 1);
 12       g.AddEdge(0, 2);
 13       g.AddEdge(1, 2);
 14       g.AddEdge(2, 0);
 15       g.AddEdge(2, 3);
 16       g.AddEdge(3, 3);
 17 
 18       List<int> traversal = g.BFS(2);
 19       foreach (var vertex in traversal)
 20       {
 21         Console.WriteLine(vertex);
 22       }
 23 
 24       Console.ReadKey();
 25     }
 26 
 27     class Edge
 28     {
 29       public Edge(int begin, int end)
 30       {
 31         this.Begin = begin;
 32         this.End = end;
 33       }
 34 
 35       public int Begin { get; private set; }
 36       public int End { get; private set; }
 37     }
 38 
 39     class Graph
 40     {
 41       private Dictionary<int, List<Edge>> _adjacentEdges
 42         = new Dictionary<int, List<Edge>>();
 43 
 44       public Graph(int vertexCount)
 45       {
 46         this.VertexCount = vertexCount;
 47       }
 48 
 49       public int VertexCount { get; private set; }
 50 
 51       public void AddEdge(int begin, int end)
 52       {
 53         if (!_adjacentEdges.ContainsKey(begin))
 54         {
 55           var edges = new List<Edge>();
 56           _adjacentEdges.Add(begin, edges);
 57         }
 58 
 59         _adjacentEdges[begin].Add(new Edge(begin, end));
 60       }
 61 
 62       public List<int> BFS(int start)
 63       {
 64         List<int> traversal = new List<int>();
 65         int current = start;
 66 
 67         // mark all the vertices as not visited
 68         bool[] visited = new bool[VertexCount];
 69         for (int i = 0; i < VertexCount; i++)
 70         {
 71           visited[i] = false;
 72         }
 73 
 74         // create a queue for BFS
 75         Queue<int> queue = new Queue<int>();
 76 
 77         // mark the current node as visited and enqueue it
 78         visited[current] = true;
 79         queue.Enqueue(current);
 80 
 81         while (queue.Count > 0)
 82         {
 83           current = queue.Dequeue();
 84 
 85           // if this is what we are looking for
 86           traversal.Add(current);
 87 
 88           // get all adjacent vertices of the dequeued vertex,
 89           // if a adjacent has not been visited, 
 90           // then mark it visited and enqueue it
 91           if (_adjacentEdges.ContainsKey(current))
 92           {
 93             foreach (var edge in _adjacentEdges[current])
 94             {
 95               if (!visited[edge.End])
 96               {
 97                 visited[edge.End] = true;
 98                 queue.Enqueue(edge.End);
 99               }
100             }
101           }
102         }
103 
104         return traversal;
105       }
106     }
107   }
108 }

參考資料

本篇文章《廣度優先搜索》由 Dennis Gao 發表自博客園,未經作者本人同意禁止任何形式的轉載,任何自動或人為的爬蟲轉載行為均為耍流氓。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM