廣度優先搜索(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)數據結構來存儲遍歷圖中節點的中間狀態,過程如下:
- 將 root 節點 Enqueue;
- Dequeue 一個節點,並檢查該節點:
- 如果該節點就是要找的目標節點,則結束遍歷,返回結果 "Found";
- 否則,Enqueue 所有直接后繼子節點(如果節點未被訪問過);
- 如果 Queue 為空,並且圖中的所有節點都被檢查過,仍未找到目標節點,則結束搜索,返回結果 "Not Found";
- 如果 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 }
參考資料
- 廣度優先搜索
- 深度優先搜索
- Breadth First Traversal for a Graph
- Depth First Traversal for a Graph
- Introduction to Algorithms 6.006 - Lecture 13
本篇文章《廣度優先搜索》由 Dennis Gao 發表自博客園,未經作者本人同意禁止任何形式的轉載,任何自動或人為的爬蟲轉載行為均為耍流氓。