当前位置: 首页 > news >正文

广度优先搜索(BFS)算法详解

一、什么是BFS?

广度优先搜索(Breadth-First Search,简称BFS)是一种用于遍历或搜索树或图的算法。它从根节点开始,先访问所有相邻节点,然后再依次访问这些相邻节点的相邻节点,以此类推,直到找到目标或遍历完整个结构。

二、算法核心思想

BFS的核心特点是按层次探索,就像水波扩散一样,总是先探索距离起点最近的节点。在迷宫问题中,这种特性保证我们找到的路径一定是最短路径。

三、BFS的工作原理

基本流程

  1. 将起始节点放入队列

  2. 从队列中取出第一个节点并检查

  3. 如果找到目标则结束搜索并返回结果

  4. 否则将它所有未访问的相邻节点加入队列

  5. 重复步骤2-4直到队列为空

关键数据结构

  • 队列(Queue):用于存储待访问的节点,保证先进先出(FIFO)的顺序

  • 访问标记(Visited):记录已访问的节点,避免重复访问

四、代码解析

1. 数据结构定义

#define N 110
#define M 10010typedef struct {int x;      // 当前点的x坐标int y;      // 当前点的y坐标int step;   // 从起点到当前点的步数
}P;
  • NM定义了数组的最大尺寸

  • P结构体表示迷宫中的一个点,包含坐标和到达该点的步数

2. 全局变量

int n, m;           // 迷宫的行数和列数
int map[N][N];      // 存储迷宫地图,0表示可通行,1表示障碍
int vis[N][N];      // 标记数组,记录哪些点已经访问过P q[M];             // 队列,用于BFS
int hh, tt = -1;    // 队列的头指针和尾指针int dx[4] = {-1,0,1,0}; // 四个方向的x偏移量
int dy[4] = {0,-1,0,1}; // 四个方向的y偏移量

3. BFS核心函数

void bfs(P p) {q[++tt] = p;    // 起点入队while (hh <= tt) { // 队列不为空时循环p = q[hh++]; // 取出队首元素// 如果到达终点,输出步数并返回if (p.x == n-1 && p.y == m-1) {printf("%d", p.step);return;}P next; // 临时存储下一个位置// 检查四个方向for (int i = 0; i < 4; i++) {next.x = p.x + dx[i];next.y = p.y + dy[i];// 如果新位置有效且未访问过if (next.x >= 0 && next.x < n && next.y >= 0 && next.y < m && map[next.x][next.y] == 0 && !vis[next.x][next.y]) {next.step = p.step + 1; // 步数加1q[++tt] = next;        // 新位置入队vis[next.x][next.y] = 1; // 标记为已访问}}}
}

4. 主函数

int main() {// 读取迷宫尺寸scanf("%d%d", &n, &m);// 读取迷宫数据for (int i = 0; i < n; i++)for (int j = 0; j < m; j++)scanf("%d", &map[i][j]);// 初始化起点P start;start.x = 0;start.y = 0;start.step = 0;// 执行BFSbfs(start);return 0;
}

 五.图解

队列与BFS的关系

队列(Queue)是一种先进先出(FIFO)的线性数据结构,这与BFS"按层次扩展"的特性完美契合。在BFS中,队列主要有以下功能:

  1. 存储待访问节点:保存已发现但尚未探索的节点

  2. 控制访问顺序:确保节点按距离起点的层次顺序被处理

  3. 维护搜索状态:记录搜索过程中的中间状态

队列操作详解

(1) 初始化队列
  • 使用数组模拟队列,定义头指针hh和尾指针tt

  • 初始时hh=0tt=-1表示空队列

(2) 入队操作
  • q[++tt] = node:尾指针后移并存入新节点

  • 保证新发现的节点排在队列末尾

(3) 出队操作
  • node = q[hh++]:取出队首节点并后移头指针

  • 确保按发现顺序处理节点

我将用更直观的方式展示广度优先搜索(BFS)如何找到迷宫的最短路径。以下是一个5×5的迷宫示例:

迷宫布局 (0=通路,1=墙壁)

(0,0) (0,1) (0,2) (0,3) (0,4)0     1     0     0     0
(1,0) (1,1) (1,2) (1,3) (1,4)0     1     0     1     0
(2,0) (2,1) (2,2) (2,3) (2,4)0     0     0     0     0
(3,0) (3,1) (3,2) (3,3) (3,4)0     1     1     1     0
(4,0) (4,1) (4,2) (4,3) (4,4)0     0     0     1     0

BFS搜索过程图解

初始状态

  • 起点: (0,0),步数=0

  • 队列: [(0,0,0)]

  • 已访问: (0,0)

[ (0,0,0) ]

第1步:处理(0,0)

  • 可移动方向:

    • ↓ (1,0): 通路,加入队列

    • → (0,1): 墙壁,跳过

  • 新队列: [(1,0,1)]

  • 已访问: (0,0), (1,0)

[ (1,0,1) ]

第2步:处理(1,0)

  • 可移动方向:

    • ↓ (2,0): 通路,加入队列

  • 新队列: [(2,0,2)]

  • 已访问: (0,0), (1,0), (2,0)

[ (2,0,2) ]

第3步:处理(2,0)

  • 可移动方向:

    • ↓ (3,0): 通路,加入队列

    • → (2,1): 通路,加入队列

  • 新队列: [(3,0,3), (2,1,3)]

  • 已访问: 添加(3,0), (2,1)

[ (3,0,3), (2,1,3) ]

第4步:处理(3,0)

  • 可移动方向:

    • ↓ (4,0): 通路,加入队列

  • 新队列: [(2,1,3), (4,0,4)]

  • 已访问: 添加(4,0)

[ (2,1,3), (4,0,4) ]

第5步:处理(2,1)

  • 可移动方向:

    • → (2,2): 通路,加入队列

  • 新队列: [(4,0,4), (2,2,4)]

  • 已访问: 添加(2,2)

[ (4,0,4), (2,2,4) ]

第6步:处理(4,0)

  • 可移动方向:

    • → (4,1): 通路,加入队列

  • 新队列: [(2,2,4), (4,1,5)]

  • 已访问: 添加(4,1)

[ (2,2,4), (4,1,5) ]

第7步:处理(2,2)

  • 可移动方向:

    • ↑ (1,2): 通路,加入队列

    • → (2,3): 通路,加入队列

  • 新队列: [(4,1,5), (1,2,5), (2,3,5)]

  • 已访问: 添加(1,2), (2,3)

[ (4,1,5), (1,2,5), (2,3,5) ]

继续处理直到找到终点

最终会找到路径到(4,4),此时步数为8。

最短路径可视化

(0,0) → (1,0) → (2,0) → (2,1) → (2,2) → (2,3) → (2,4) → (3,4) → (4,4)

步数: 8

关键点说明

  1. 队列特性:先进先出(FIFO)保证按层次搜索

  2. 步数记录:每个节点存储从起点到该点的准确步数

  3. 访问标记:防止重复访问和环路

  4. 方向顺序:上、左、下、右的顺序检查相邻格子

六.细节问题

p.step是如何更新的?

在BFS中,队列 (q) 存储待处理的点,而 p 始终是当前正在处理的队列头部节点。步数更新的秘密在于:

  1. p 的来源
    p 是从队列头部取出的节点(p = q[hh++]),它的 step 值在入队时就已经确定,且之后不会被修改

  2. next.step 的生成
    每个新探索的相邻点 next 的步数是通过 p.step + 1 计算的,表示从 p 移动一步到达 next

  3. 输出时机
    当 p 是终点时,直接输出 p.step,因为此时 p 的步数已经是起点到终点的最短步数。

七、算法执行流程

  1. 初始化:将起点(0,0)加入队列,步数为0

  2. 循环处理队列

    • 取出队首元素

    • 如果是终点,返回当前步数

    • 否则检查四个方向的相邻点

    • 将有效的、未访问的相邻点加入队列,步数+1

  3. 终止条件:队列为空或找到终点

八、关键点分析

  1. 队列的作用:保证按层次探索,先进先出(FIFO)

  2. 访问标记数组:防止重复访问,确保每个点只处理一次

  3. 步数更新:每个新点的步数=当前点步数+1

  4. 方向处理:通过方向数组实现上、左、下、右的探索

九、实际应用

这种BFS算法可以应用于:

  1. 迷宫最短路径求解

  2. 社交网络中的最短关系链查找

  3. 网页爬虫的层级抓取

  4. 游戏中的AI路径规划

相关文章:

  • openinstall+Web-to-app归因解决方案
  • 在linux系统中安装ktransformersV0.24部署deepseek r1模型并用open AI风格调用
  • SpringMVC 静态资源处理 mvc:default-servlet-handler
  • 01_Long比较值 类型相同值不同
  • 联想昭阳笔记本 风扇一键静音优化操作指南
  • RuoYi-Vue项目Docker镜像构建、推送与部署完整流程
  • FEKO许可安装
  • CF2096G Wonderful Guessing Game 构造
  • 强制缓存vs协商缓存
  • 2025系统架构师---黑板架构风格
  • element通过业务按钮点击导入,调用el-upload的导入方法
  • 日本IT|UIUX主要的工作都是哪些?及职业前景
  • 关于PyQt5信号槽机制的解析
  • Spring AOP 详解
  • ARCGIS PRO 在地图中飞行
  • (done) 吴恩达版提示词工程 9. 总结 (就是复述一遍前面的内容,以及建议你基于LLM开发应用程序)
  • 8、HTTPD服务--CGI机制
  • linux两个特殊的宏 _RET_IP_ 和_THIS_IP_ 实现
  • 第15节:传统分类模型-K近邻(KNN)算法
  • 【文献速递】snoRNA-SNORD113-3/ADAR2通过对PHKA2的A-to-I编辑影响胶质母细胞瘤糖脂代谢
  • 夜读丨怀念那个写信的年代
  • 牛市早报|今年国内核电项目审批首次开闸,离境退税起退点下调
  • “中国游”带火“中国购”,“即买即退”让外国游客购物更丝滑
  • 最高法知识产权法庭:6年来新收涉外案件年均增长23.2%
  • 专访|白俄罗斯共产党中央第一书记瑟兰科夫:只有大家联合起来,才能有效应对当前危机所带来的冲击
  • 还山记——走进山水、感受山水艺术的魅力