「」至此。关于其他用法，请见「**BFS (消歧义)**」。

| 图与树搜索算法 |
| --- |
| * α–β
* A\*
* B\*（英语：）
* 回溯
* 集束（英语：）
* 贝尔曼-福特
* 最佳优先（英语：）
* 双向
* Borůvka（英语：）
* 分支限界
* BFS
* 大英博物馆
* D\*（英语：）
* DFS
* 深度限制（英语：）
* 迪杰斯特拉
* Edmonds（英语：）
* 弗洛伊德
* 边缘搜索
* 爬山
* IDA\*（英语：）
* 迭代加深
* Johnson（英语：）
* 跳点（英语：）
* 克鲁斯克尔
* 词典BFS（英语：）
* LPA\*（英语：）
* 普里姆
* SMA\*（英语：）
* 最短路径快速
 |
| 
分类 |
| * 图算法
* 搜索算法
* 算法列表（英语：）
 |
| 
相关主题 |
| * 动态规划
* 图的遍历
* 树的遍历
 |
| * 
* 论
* 编
 |

**广度优先搜索算法**（英語：，縮寫：**BFS**），又譯作**寬度優先搜索**，或**橫向優先搜索**，是一種圖形搜索演算法。簡單的說，BFS是從根節點開始，沿着树的宽度遍历树的节点。如果所有节点均被访问，则算法中止。广度优先搜索的实现一般采用open-closed表。

作法
--

BFS是一種暴力搜索算法，目的是系統地展開並檢查圖中的所有節點，以找尋結果。換句話說，它並不考慮結果的可能位址，徹底地搜索整張圖，直到找到結果為止。BFS並不使用經驗法則演算法。

從演算法的觀點，所有因為展開節點而得到的子節點都會被加進一個先進先出的队列中。一般的實作裡，其鄰居節點尚未被檢驗過的節點會被放置在一個被稱為 *open* 的容器中（例如佇列或是），而被檢驗過的節點則被放置在被稱為 *closed* 的容器中。（open-closed表）

|  |  |  |
| --- | --- | --- |
| 以德國城市為範例的地圖。城市間有數條道路相連接。 | 從法蘭克福開始執行廣度優先搜索算法，所產生的廣度優先搜索算法樹。 | 廣度優先搜索算法的動畫範例 |

實作方法
----

1. 首先將根節點放入队列中。
2. 從队列中取出第一個節點，並檢驗它是否為目標。
	* 如果找到目標，則結束搜尋並回傳結果。
	* 否則將它所有尚未檢驗過的直接子節點加入队列中。
3. 若队列為空，表示整張圖都檢查過了——亦即圖中沒有欲搜尋的目標。結束搜尋並回傳「找不到目標」。
4. 重複步驟2。

```

 s为初始点
 R:=\{ s \},Q:=\{ s \},T=\empty
 while Q \ne \empty
     從Q中選一點 v /* 若改選最後插入進Q的點，則為深度遍歷,可以说後進先出。*/
     if \exists w \in N(v) \setminus R then    /* N(v):v的邻接点 */
         Q:=Q \cup \{ w \}
         R:=R \cup \{ w \}
         T:=T \cup \{ vw \}
     else Q:=Q \setminus \{ w \}
 return H=(R,T)
```

### C 的实例

```
/\*
 ADDQ (Q, p) - p PUSH 入 Q
 DELQ (Q) - POP Q 并返回 Q 顶
 FIRSTADJ (G,v) - v 的第一个邻接点，找不到则返回 -1
 NEXTADJ (G,v) - v 的下一个邻接点，找不到则返回 -1
 VISIT (v) - 访问 v
 visited [] - 是否已访问
\*/

// 广度优先搜索算法
void BFS(VLink G[], int v) {
 int w;
 VISIT(v); // 访问 v 并入队
 visited[v] = 1;
 ADDQ(Q, v);
 // 对队列 Q 的各元素
 while (!EMPTYQ(Q)) {
 v = DELQ(Q);
 w = FIRSTADJ(G, v);
 do {
 // 进行访问和入队
 if (visited[w] == 0) {
 VISIT(w);
 ADDQ(Q, w);
 visited[w] = 1;
 }
 } while ((w = NEXTADJ(G, v)) != -1);
 }
}

// 对图G=(V,E)进行广度优先搜索的主算法
void TRAVEL\_BFS(VLink G[], bool visited[], int n) {
 // 清零标记数组
 for (int i = 0; i < n; ++i)
 visited[i] = 0;
 for (int i = 0; i < n; ++i)
 if (visited[i] == 0)
 BFS(G, i);
}

```

### C++ 的實作

(這個例子僅針對Binary Tree)  

定义一个结构体来表达一个節點的结构：

```
struct node {
 int self; //数据
 node \*left; //左节点
 node \*right; //右节点
};

```

那么，我们在搜索一个树的时候，从一个节点开始，能首先获取的是它的两个子节点。例如：

```
   A
B     C
```

A是第一个访问的，然后顺序是B和C；然后再是B的子节点，C的子节点。那么我们怎么来保证这个顺序呢？

这里就应该用queue資料結構，因为queue採用先进先出( first-in-first-out )的顺序。

使用C++的STL函式庫，下面的能帮助理解：

```
 std::queue<node \*> visited, unvisited;
node nodes[9];
node \*current;

unvisited.push(&nodes[0]); // 先把root放入unvisited queue

while (!unvisited.empty()) { // 只有unvisited不空
 current = (unvisited.front()); // 目前應該檢驗的
 if (current->left != NULL)
 unvisited.push(current->left); // 把左邊放入queue中
 if (current->right != NULL) // 右邊壓入。因為QUEUE是一個先進先出的結構构，所以即使後面再壓其他东西，依然會先訪問這個。
 unvisited.push(current->right);
 visited.push(current);
 cout << current->self << endl;
 unvisited.pop();
}

```

特性
--

### 空間複雜度

因為所有節點都必須被儲存，因此BFS的空間複雜度為{\displaystyle O(|V|+|E|)}，其中{\displaystyle |V|}是節點的數目，而{\displaystyle |E|}是圖中邊的數目。註：另一種說法稱BFS的空間複雜度為O(B^M)，其中B是最大分支係數，而M是樹的最長路徑長度。由於對空間的大量需求，因此BFS並不適合解非常大的問題，對於類似的問題，應用IDDFS以達節省空間的效果。

### 時間複雜度

最差情形下，BFS必須尋找所有到可能節點的所有路徑，因此其時間複雜度為{\displaystyle O(|V|+|E|)}，其中{\displaystyle |V|}是節點的數目，而{\displaystyle |E|}是圖中邊的數目。

### 完全性

廣度優先搜索演算法具有完全性。這意指無論圖形的種類如何，只要目標存在，則BFS一定會找到。然而，若目標不存在，且圖為無限大，則BFS將不收斂（不會結束）。

### 最佳解

若所有邊的長度相等，廣度優先搜索演算法是最佳解——亦即它找到的第一個解，距離根節點的邊數目一定最少；但對一般的圖來說，BFS並不一定回傳最佳解。這是因為當圖形為加權圖（亦即各邊長度不同）時，BFS仍然回傳從根節點開始，經過邊數目最少的解；而這個解距離根節點的距離不一定最短。這個問題可以使用考慮各邊權值，BFS的改良演算法成本一致搜尋法來解決。然而，若非加權圖形，則所有邊的長度相等，BFS就能找到最近的最佳解。

應用
--

廣度優先搜索演算法能用來解決圖論中的許多問題，例如：

* 尋找圖中所有連接元件（Connected Component）。一個連接元件是圖中的最大相連子圖。
* 尋找連接元件中的所有節點。
* 尋找非加權圖中任兩點的最短路徑。
* 測試一圖是否為二分圖。
* （Reverse）Cuthill–McKee演算法

### 尋找連接元件

由起點開始，執行廣度優先搜索演算法後所經過的所有節點，即為包含起點的一個連接元件。

### 測試是否二分圖

BFS可以用以測試二分圖。從任一節點開始搜尋，並在搜尋過程中給節點不同的標籤。例如，給開始點標籤0，開始點的所有鄰居標籤1，開始點所有鄰居的鄰居標籤0……以此類推。若在搜尋過程中，任一節點有跟其相同標籤的鄰居，則此圖就不是二分圖。若搜尋結束時這種情形未發生，則此圖為一二分圖。

### 應用於電腦遊戲中平面網格

BFS可用來解決電腦遊戲（例如即時策略遊戲）中找尋路徑的問題。在這個應用中，使用平面網格來代替圖形，而一個格子即是圖中的一個節點。所有節點都與它的鄰居（上、下、左、右、左上、右上、左下、右下）相接。

值得一提的是，當這樣使用BFS演算法時，首先要先檢驗上、下、左、右的鄰居節點，再檢驗左上、右上、左下、右下的鄰居節點。這是因為BFS趨向於先尋找斜向鄰居節點，而不是四方的鄰居節點，因此找到的路徑將不正確。BFS應該先尋找四方鄰居節點，接著才尋找斜向鄰居節點1。

參見
--

* 先验算法
* 深度優先搜索

參考資料
----

* Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein], *Introduction to Algorithms*, Second Edition. MIT Press and McGraw-Hill, 2001. ISBN 0-262-03293-7. Section 22.2: Breadth-first search, pp. 531–539.

外部連結
----

* （英文） 資料結構與演算法字典：廣度優先搜索 （页面存档备份，存于）
* （英文） C++ Boost Graph函式庫：廣度優先搜索 （页面存档备份，存于）
* （英文） 深度與廣度優先搜索：解釋與原始碼 （页面存档备份，存于）
* （英文） BFS 動畫說明 （页面存档备份，存于）


|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
算法 |
| --- |
|  |
| 排序 | 

|  |  |
| --- | --- |
| 比较排序 | 
* 冒泡排序
* 选择排序
* 插入排序
* 希尔排序
* 快速排序
* 归并排序
* 堆排序
* 鸡尾酒排序
* 梳排序
* 侏儒排序
* 图书馆排序
* 内省排序
* 奇偶排序

 |
|  |
| 线性时间排序 | 
* 鸽巢排序
* 基数排序
* 計數排序
* 桶排序

 |
|  |
| 并行排序 | 
* 排序网络（英语：）
* Batcher归并网络

 |
|  |
| 不实用的 | 
* Bogo排序
* 臭皮匠排序

 |
|  |
| 图 | 
* 拓撲排序

 |

 |
|  |
| 搜索 | 

|  |  |
| --- | --- |
| 列表 | 
* 线性搜索
* 二分搜索
* 插值搜尋

 |
|  |
| 树・图 | 
* 广度优先搜索
	+ 最良優先搜索（英语：）
	+ 均一开销搜索
	+ A\*
* 深度优先搜索
	+ 迭代深化深度优先搜索
	+ 深度限制搜索（日语：）
	+ 双向搜索
* 分枝限定法（英语：）

 |
|  |
| 字符串 | 
* KMP算法
* 博耶-穆尔字符串搜索算法
* AC自动机算法
* 拉宾-卡普算法
* bitap算法

 |

 |
|  |
| 最短路问题 | 
* 戴克斯特拉算法
* 贝尔曼-福特算法
* A\*搜尋演算法
* Floyd-Warshall算法

 |
|  |
| 最小生成树 | 
* 普林姆算法
* 克鲁斯克尔演算法

 |
|  |
| 最大流最小割 | 
* 福特-富尔克森算法
* 埃德蒙兹-卡普算法
* 迪尼茨算法

 |
|  |
| 线性规划 | 
* 单纯形法
* 卡马卡尔算法（英语：）

 |
|  |
| 順序統計量 | 
* 选择算法
* 中位数的中位数（英语：）

 |
|  |
| 種類 | 
* 精确算法
* 近似算法
* 随机化算法

 |
|  |
| 其他 | 
* 分治法
* 动态规划
* 贪心算法

 |
|  |
| **Category:算法** |

 |