广度优先搜索详解(BFS)

一、什么是广度优先搜索

广度优先搜索(Breadth-First Search,简称BFS),也称为宽度优先搜索,是图与树结构中最基础的遍历算法之一。 与深度优先搜索(DFS)一路走到头再回溯 的遍历逻辑不同,BFS采用按层遍历的策略:从起点出发,先遍历所有与起点距离为1的节点,再遍历距离为2的节点,以此类推,逐层向外扩展,直到遍历完所有可达节点。

二、BFS的核心原理

  1. 核心数据结构:队列 BFS的「按层遍历」特性,完全依赖队列(Queue)的「先进先出(FIFO)」特性实现: 先进入队列的节点,会被优先处理,保证同一层的节点按顺序被遍历后进入队列的下一层节点,会排在当前层节点之后,保证遍历的层级顺序
  2. 核心规则:标记访问 图与树中可能存在环路或重复路径,因此BFS必须维护一个访问标记数组,记录节点是否已经被遍历过,避免重复处理导致死循环。

三、BFS的标准实现步骤

BFS的实现逻辑高度固定,所有场景下的BFS都遵循以下核心步骤:

  1. 初始化:创建队列,将起点加入队列,同时标记起点为已访问
  2. 循环处理:当队列不为空时,重复执行后续操作
  3. 取出队首:弹出队列最前端的节点,作为当前处理节点
  4. 处理节点:执行当前节点的业务逻辑(如输出、统计、计算距离等)
  5. 扩展邻接:遍历当前节点的所有邻接节点,将未访问的节点标记后加入队列
  6. 结束:队列为空时,所有可达节点遍历完成

四、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的「按层遍历」特性,使其在以下场景中成为最优解法:

  1. 树的层序遍历 层序遍历是树的基础遍历方式,要求按从上到下、同一层从左到右的顺序输出节点。BFS天然符合层序遍历的要求,是该问题的标准解法。
  2. 无权图的单源最短路径 对于所有边权都为1的无权图,BFS第一次到达某个节点时的距离,就是起点到该节点的最短距离。这是BFS最核心的应用之一,也是所有最短路算法中时间复杂度最低的解法。
  3. 连通性判断 判断图中两个节点是否连通,或统计图中连通块的数量,都可以通过BFS实现:从一个节点出发BFS,所有能遍历到的节点都属于同一个连通块。
  4. 树上奇偶层统计 对于树结构,BFS遍历得到的节点深度,天然具有奇偶交替的特性,因此可以快速统计所有深度为偶数/奇数的节点数量,是解决树上路径奇偶性问题的基础。

六、经典例题详解

例题1:树的子树大小计算

题目要求

给定一棵根为1的树,输出每个节点的子树大小。

解法思路

  1. 先通过BFS计算每个节点的深度,确定树的层级关系
  2. 按深度从大到小的顺序(从叶子到根),累加每个节点的子节点大小,即可得到子树大小

例题2:树上漫步(GESP202503六级)

题目要求

对于树上每个节点,求从该节点出发,走偶数步能到达的节点总数。

解法思路

  1. 从根节点1出发BFS,统计所有深度为偶数的节点总数`cnt0`,深度为奇数的节点总数`cnt1`
  2. 由于树是二分图,相邻节点的深度奇偶性必然相反,因此: - 深度为偶数的节点,走偶数步能到达的节点数 = `cnt0` - 深度为奇数的节点,走偶数步能到达的节点数 = `cnt1`
  3. 仅需一次BFS即可计算所有节点的答案,时间复杂度O(n)

七、BFS的复杂度与特点

时间复杂度

每个节点仅入队一次,每条边仅被遍历一次 - 总时间复杂度为 O(n + m),其中n为节点数,m为边数,属于线性时间复杂度的遍历算法

空间复杂度

队列最多存储一层的节点,最坏情况下为O(n) - 访问标记、距离数组的空间复杂度为O(n) - 总空间复杂度为 O(n)

八、注意事项

  1. 标记访问:无标记的BFS会在有环图中陷入死循环
  2. 建图规则正确:无向图/树必须双向加边,有向图仅需单向加边
  3. 数组大小匹配:根据题目数据范围调整数组大小,避免越界
相关推荐
金銀銅鐵1 小时前
[Python] 扩展欧几里得算法
python·数学·算法
To_OC4 小时前
LC 200 岛屿数量:经典 DFS 入门题,我第一次写居然连方向都搞错了
javascript·算法·leetcode
To_OC20 小时前
LC 128 最长连续序列:别上来就排序,O (n) 解法才是这题的灵魂
javascript·算法·leetcode
05Kevin1 天前
lk每日冒险题--数据结构6.27
算法
To_OC2 天前
从一次栈溢出报错说起,我把递归彻底扒明白了
javascript·算法·程序员
千纸鹤安安2 天前
千问Qwen-AgentWorld来了:一个语言模型搞定七大Agent场景,GPT-5.4都输了
算法
七牛开发者2 天前
MCP 到底是什么?为什么 Agent 都想接上它
算法·aigc·agent