BFS(Breadth-First Search)技术文档

—— 基于迷宫问题与消消乐连通块统计的工程实践

一、BFS 基本概念

广度优先搜索(BFS,Breadth-First Search) 是一种按“层级”向外扩展的图遍历算法。
其核心特征是:

从起点出发,先访问所有距离为 1 的节点,再访问距离为 2 的节点,以此类推。
BFS 通常依赖 队列(Queue) 实现,天然具备以下优势:

  • 遍历顺序稳定
  • 不会出现递归栈溢出
  • 非常适合:
    • 最短路径
    • 连通块统计
    • 大规模网格搜索

二、BFS 在二维网格问题中的通用模型

1. 网格抽象为图

  • 每个格子:一个节点
  • 上下左右:边
  • 是否可扩展:由题目规则决定(颜色是否相同、是否不同等)

2. BFS 通用模板(二维网格)

Queue<Node> queue = new Queue<Node>();
queue.Enqueue(start);
mark[start] = true;

while (queue.Count > 0)
{
    Node cur = queue.Dequeue();
    for (四个方向)
    {
        if (合法 && 未访问 && 满足条件)
        {
            mark[next] = true;
            queue.Enqueue(next);
        }
    }
}

三、BFS 示例一:迷宫 01 交替连通块问题

1. 问题描述

  • 给定一个 n × n 的迷宫
  • 每个格子为 '0''1'
  • 连通条件
    • 上下左右相邻
    • 相邻格子的值必须不同(01 交替)
  • 多次查询某个格子所在连通块的大小

2. 设计目标

  • 对迷宫进行一次 BFS 预处理
  • 标记所有 01 交替的连通块
  • 查询阶段 O(1) 返回答案

3. 核心数据结构

char[,] grid;     // 迷宫地图
int[,] compId;    // 每个格子的连通块编号
int[] compSize;   // 每个连通块的大小

含义说明:

  • compId[x][y] = k:该格子属于第 k 个连通块
  • compSize[k]:第 k 个连通块的格子数量

4. BFS 核心实现逻辑

static void BFS(int sx, int sy, int id)
{
    Queue<Point> queue = new Queue<Point>();
    queue.Enqueue(new Point(sx, sy));
    compId[sx, sy] = id;
    compSize[id] = 1;

    while (queue.Count > 0)
    {
        Point p = queue.Dequeue();
        for (int i = 0; i < 4; i++)
        {
            int nx = p.x + dx[i];
            int ny = p.y + dy[i];

            if (nx < 0 || nx >= n || ny < 0 || ny >= n)
                continue;

            // 01 交替 + 未访问
            if (grid[p.x, p.y] != grid[nx, ny] && compId[nx, ny] == 0)
            {
                compId[nx, ny] = id;
                compSize[id]++;
                queue.Enqueue(new Point(nx, ny));
            }
        }
    }
}

5. 算法本质分析

  • BFS 扩展的是 “合法的相邻状态”
  • 连通块的定义只体现在 扩展条件
  • BFS 本身不关心业务规则

四、BFS 示例二:消消乐连通块统计(同色)

1. 问题描述

  • 棋盘为 n × n
  • 每个格子有一个颜色(整数)
  • 消除规则:
    • 上下左右相邻
    • 颜色相同
  • 多次点击,询问消除数量

2. 核心优化思想:预处理 + 查询分离

如果每次点击都 BFS:

  • 单次:O(n²)
  • m 次:O(mn²)(不可接受)
    解决方案:

使用 BFS 对所有连通块进行一次性预处理。


3. BFS 预处理流程

for 每一个格子 (i, j)
    if compId[i][j] == 0
        compCnt++
        BFS(i, j, compCnt)

4. BFS 实现代码(同色连通)

static void BFS(int sx, int sy, int id)
{
    Queue<Pos> queue = new Queue<Pos>();
    queue.Enqueue(new Pos(sx, sy));
    compId[sx, sy] = id;
    compSize[id] = 1;

    while (queue.Count > 0)
    {
        Pos p = queue.Dequeue();
        for (int i = 0; i < 4; i++)
        {
            int nx = p.x + dx[i];
            int ny = p.y + dy[i];

            if (nx < 0 || nx >= n || ny < 0 || ny >= n)
                continue;

            if (grid[p.x, p.y] == grid[nx, ny] && compId[nx, ny] == 0)
            {
                compId[nx, ny] = id;
                compSize[id]++;
                queue.Enqueue(new Pos(nx, ny));
            }
        }
    }
}

5. 查询阶段(O(1))

int id = compId[x][y];
Console.WriteLine(compSize[id]);

五、BFS 连通块模型统一总结

1. 抽象结构

元素 含义
节点 网格中的格子
上下左右
扩展条件 颜色相同 / 不同
连通块 满足规则的最大集合

2. BFS 与 DFS 在工程中的选择

对比项 BFS DFS
是否递归
栈溢出风险
稳定性
大规模网格 更适合 有风险

六、复杂度分析

N = n × n

  • 预处理时间复杂度:O(N)
  • 单次查询时间复杂度:O(1)
  • 总时间复杂度:O(N + m)
  • 空间复杂度:O(N)

七、核心认知总结

BFS 的真正价值不在“搜索”,而在:

用一次完整遍历,换取所有查询的常数时间响应
当你开始自然使用:

  • compId
  • compSize
  • “预处理 + 查询分离”
    说明你已经掌握了 工程级 BFS 思维

八、一句话模板总结

凡是:网格 + 连通规则 + 多次查询
第一反应:BFS / DFS 预处理连通块


posted @ 2025-12-18 16:52  高山仰止666  阅读(4)  评论(0)    收藏  举报