一、什么是广度优先搜索
广度优先搜索(Breadth-First Search,简称BFS),也称为宽度优先搜索,是图与树结构中最基础的遍历算法之一。 与深度优先搜索(DFS)一路走到头再回溯 的遍历逻辑不同,BFS采用按层遍历的策略:从起点出发,先遍历所有与起点距离为1的节点,再遍历距离为2的节点,以此类推,逐层向外扩展,直到遍历完所有可达节点。
二、BFS的核心原理
- 核心数据结构:队列 BFS的「按层遍历」特性,完全依赖队列(Queue)的「先进先出(FIFO)」特性实现: 先进入队列的节点,会被优先处理,保证同一层的节点按顺序被遍历后进入队列的下一层节点,会排在当前层节点之后,保证遍历的层级顺序
- 核心规则:标记访问 图与树中可能存在环路或重复路径,因此BFS必须维护一个访问标记数组,记录节点是否已经被遍历过,避免重复处理导致死循环。
三、BFS的标准实现步骤
BFS的实现逻辑高度固定,所有场景下的BFS都遵循以下核心步骤:
- 初始化:创建队列,将起点加入队列,同时标记起点为已访问
- 循环处理:当队列不为空时,重复执行后续操作
- 取出队首:弹出队列最前端的节点,作为当前处理节点
- 处理节点:执行当前节点的业务逻辑(如输出、统计、计算距离等)
- 扩展邻接:遍历当前节点的所有邻接节点,将未访问的节点标记后加入队列
- 结束:队列为空时,所有可达节点遍历完成
四、BFS通用代码模板
以下为C++语言的标准BFS实现模板,适用于树、无向图、有向图的绝大多数场景:
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=2e5+10;
vector<ll> adj[MAXN];
bool vis[MAXN];
ll dis[MAXN];
void bfs(ll start){
queue<ll> q;
q.push(start);
vis[start]=true;
dis[start]=0;
while(!q.empty()){
ll u=q.front();
q.pop();
for(int i=0;i<adj[u].size();i++){
ll v=adj[u][i];
if(!vis[v]){
vis[v]=true;
dis[v]=dis[u]+1;
q.push(v);
}
}
}
}
int main(){
ll n;
cin>>n;
for(ll i=1;i<=n-1;i++){
ll u,v;
cin>>u>>v;
adj[u].push_back(v);
adj[v].push_back(u);
}
bfs(1);
return 0;
}
五、BFS的经典应用场景
BFS的「按层遍历」特性,使其在以下场景中成为最优解法:
- 树的层序遍历 层序遍历是树的基础遍历方式,要求按从上到下、同一层从左到右的顺序输出节点。BFS天然符合层序遍历的要求,是该问题的标准解法。
- 无权图的单源最短路径 对于所有边权都为1的无权图,BFS第一次到达某个节点时的距离,就是起点到该节点的最短距离。这是BFS最核心的应用之一,也是所有最短路算法中时间复杂度最低的解法。
- 连通性判断 判断图中两个节点是否连通,或统计图中连通块的数量,都可以通过BFS实现:从一个节点出发BFS,所有能遍历到的节点都属于同一个连通块。
- 树上奇偶层统计 对于树结构,BFS遍历得到的节点深度,天然具有奇偶交替的特性,因此可以快速统计所有深度为偶数/奇数的节点数量,是解决树上路径奇偶性问题的基础。
六、经典例题详解
例题1:树的子树大小计算
题目要求
给定一棵根为1的树,输出每个节点的子树大小。
解法思路
- 先通过BFS计算每个节点的深度,确定树的层级关系
- 按深度从大到小的顺序(从叶子到根),累加每个节点的子节点大小,即可得到子树大小
例题2:树上漫步(GESP202503六级)
题目要求
对于树上每个节点,求从该节点出发,走偶数步能到达的节点总数。
解法思路
- 从根节点1出发BFS,统计所有深度为偶数的节点总数`cnt0`,深度为奇数的节点总数`cnt1`
- 由于树是二分图,相邻节点的深度奇偶性必然相反,因此: - 深度为偶数的节点,走偶数步能到达的节点数 = `cnt0` - 深度为奇数的节点,走偶数步能到达的节点数 = `cnt1`
- 仅需一次BFS即可计算所有节点的答案,时间复杂度O(n)
七、BFS的复杂度与特点
时间复杂度
每个节点仅入队一次,每条边仅被遍历一次 - 总时间复杂度为 O(n + m),其中n为节点数,m为边数,属于线性时间复杂度的遍历算法
空间复杂度
队列最多存储一层的节点,最坏情况下为O(n) - 访问标记、距离数组的空间复杂度为O(n) - 总空间复杂度为 O(n)
八、注意事项
- 标记访问:无标记的BFS会在有环图中陷入死循环
- 建图规则正确:无向图/树必须双向加边,有向图仅需单向加边
- 数组大小匹配:根据题目数据范围调整数组大小,避免越界