一、什么是广度优先遍历(BFS)?
广度优先遍历(Breadth-First Search,简称 BFS)是一种层级展开的搜索策略。
其核心思想是:先访问当前层的所有节点,再访问下一层的节点。常借助队列(Queue)实现,保证先入先出,按层推进。
二、BFS 的应用场景
应用场景 |
描述 |
树的层序遍历 |
从上到下、从左到右访问所有节点 |
图的最短路径 |
在无权图中找起点到终点的最短路径 |
二维数组搜索(如迷宫) |
最少步数、感染扩散、岛屿问题 |
多源 BFS |
同时从多个起点扩展,如火焰蔓延 |
三、BFS vs DFS 的区别
特性 |
BFS(广度优先) |
DFS(深度优先) |
访问方式 |
一层一层遍历 |
一条路走到底 |
实现结构 |
队列(FIFO) |
栈或递归(LIFO) |
找最短路径 |
是(无权图/网格) |
否(不能保证最短) |
空间复杂度 |
O(w),w为最宽层节点数 |
O(h),h为最大深度 |
四、BFS 在树结构中的应用:层序遍历
示例:层序遍历二叉树
go
复制代码
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
Go 实现(按层收集每层元素)
go
复制代码
func levelOrder(root *TreeNode) [][]int {
var result [][]int
if root == nil {
return result
}
queue := []*TreeNode{root}
for len(queue) > 0 {
size := len(queue)
level := []int{}
for i := 0; i < size; i++ {
node := queue[0]
queue = queue[1:]
level = append(level, node.Val)
if node.Left != nil {
queue = append(queue, node.Left)
}
if node.Right != nil {
queue = append(queue, node.Right)
}
}
result = append(result, level)
}
return result
}
示例输入:
ini
复制代码
// 构建如下树:
// 1
// / \
// 2 3
// / \ \
// 4 5 6
root := &TreeNode{Val: 1}
root.Left = &TreeNode{Val: 2}
root.Right = &TreeNode{Val: 3}
root.Left.Left = &TreeNode{Val: 4}
root.Left.Right = &TreeNode{Val: 5}
root.Right.Right = &TreeNode{Val: 6}
fmt.Println(levelOrder(root))
// 输出:[[1], [2,3], [4,5,6]]
五、BFS 在图结构中的应用:最短路径
我们以无向图为例,从起点出发,寻找所有节点的最短路径长度。
图结构定义:
go
复制代码
type Graph struct {
adj map[int][]int
}
func NewGraph() *Graph {
return &Graph{adj: make(map[int][]int)}
}
func (g *Graph) AddEdge(u, v int) {
g.adj[u] = append(g.adj[u], v)
g.adj[v] = append(g.adj[v], u) // 无向图
}
BFS 实现最短路径(起点到所有点)
go
复制代码
func BFSShortestPaths(g *Graph, start int) map[int]int {
dist := make(map[int]int)
visited := make(map[int]bool)
queue := []int{start}
dist[start] = 0
visited[start] = true
for len(queue) > 0 {
node := queue[0]
queue = queue[1:]
for _, neighbor := range g.adj[node] {
if !visited[neighbor] {
dist[neighbor] = dist[node] + 1
visited[neighbor] = true
queue = append(queue, neighbor)
}
}
}
return dist
}
示例调用:
css
复制代码
g := NewGraph()
g.AddEdge(1, 2)
g.AddEdge(1, 3)
g.AddEdge(2, 4)
g.AddEdge(3, 5)
g.AddEdge(4, 5)
dist := BFSShortestPaths(g, 1)
fmt.Println(dist) // 输出每个节点到1的最短路径长度
六、BFS 在二维数组上的应用:最短路径 or 感染扩散
经典问题:迷宫最短路径
go
复制代码
type Point struct {
X, Y int
}
func bfsMaze(grid [][]int, start, end Point) int {
if grid[start.X][start.Y] == 1 {
return -1
}
m, n := len(grid), len(grid[0])
visited := make([][]bool, m)
for i := range visited {
visited[i] = make([]bool, n)
}
dirs := []Point{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}
queue := []Point{start}
visited[start.X][start.Y] = true
steps := 0
for len(queue) > 0 {
size := len(queue)
for i := 0; i < size; i++ {
curr := queue[0]
queue = queue[1:]
if curr == end {
return steps
}
for _, d := range dirs {
nx, ny := curr.X+d.X, curr.Y+d.Y
if nx >= 0 && ny >= 0 && nx < m && ny < n && grid[nx][ny] == 0 && !visited[nx][ny] {
visited[nx][ny] = true
queue = append(queue, Point{nx, ny})
}
}
}
steps++
}
return -1
}
七、复杂度分析
- • 时间复杂度 :O(n)
n 为图中节点数(或矩阵中格子数)
- • 空间复杂度 :O(n)
用于存储访问状态、队列
八、典型题目举例(LeetCode)
题目 |
描述 |
LeetCode 102 层序遍历 |
二叉树的层级展开 |
LeetCode 200 岛屿数量 |
类似 BFS 求二维图中连通区域 |
LeetCode 542 01矩阵 |
多源 BFS 计算每个 1 到最近 0 的距离 |
LeetCode 752 打开转盘锁 |
最少步数解锁问题 |
九、常见问题与调试技巧
- • ❗ 队列顺序不能乱,要始终保证 FIFO(先进先出)
- • ❗ 图结构必须标记访问状态,避免重复访问
- • ✔ BFS 是解决"最少步数"问题的首选
- • ✔ 多源同时扩展,用多个起点一起入队即可
十、总结
项目 |
内容说明 |
算法类型 |
广度优先搜索 |
核心结构 |
队列(Queue) |
最适用问题 |
最短路径、层序遍历、感染传播问题 |
数据结构支持 |
树、图、二维数组(网格) |
空间复杂度 |
O(n) |