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

广度优先搜索(BFS)算法详解——以走迷宫问题为例

引言:当算法遇见迷宫

想象你置身于一个复杂的迷宫,如何在最短时间内找到出口?这个问题不仅存在于童话故事中,更是计算机科学中经典的路径搜索问题。本文将带你通过走迷宫问题,深入理解广度优先搜索(BFS)这一强大的算法工具。


一、BFS算法原理剖析

1.1 BFS & DFS

在这里插入图片描述

1.2 算法核心思想

广度优先搜索采用"层层推进"的策略:

  • 使用队列数据结构管理待探索节点
  • 在每一个点处,所有可能到达的不重复的下一个节点都会被添加到队列中

简单来说,在遇到岔路时,会进行分身,两边同时进行探索。

1.3 算法实现框架

void bfs(起点) {
    初始化队列
    标记起点已访问
    
    while (队列不为空) {
        取出队首节点
        if (到达终点) {
            返回结果
        }
        
        for (所有相邻节点) {
            if (节点合法且未访问) {
                标记已访问
                加入队列
            }
        }
    }
}

1.4 算法特性分析

  • 时间复杂度 O ( V + E ) O(V + E) O(V+E) V V V为顶点数, E E E为边数)
  • 空间复杂度 O ( V ) O(V) O(V)
  • 优势:保证找到最短路径
  • 局限:空间开销较大

二、走迷宫问题建模

2.1 问题描述

给定一个 n ∗ m n*m nm的迷宫:

  • 0 0 0表示可通行的路径
  • 1 1 1表示障碍物
  • 从左上角 ( 1 , 1 ) (1,1) (1,1)出发,寻找到达右下角 ( n , m ) (n,m) (n,m)的最短路径

2.2 问题转化

将迷宫建模为图:

  • 节点:每个可通行的格子
  • :相邻可通行格子之间的连接
  • 权重:每条边的权重为 1 1 1

三、BFS解法的精妙实现

ACWing 走迷宫

3.1 关键数据结构

  • 队列:存储待探索节点
  • 方向数组:定义移动方向
  • 访问标记:记录已访问节点(这里标记数组和迷宫地图可以进行公用)
#include <iostream>
#include <queue>
using namespace std;

const int MAXN = 107; // 最大地图尺寸
int maze[MAXN][MAXN]; // 迷宫地图
int n, m;             // 迷宫行数和列数

// 定义节点结构
struct Node {
    int x, y;   // 当前坐标
    int steps;  // 已走步数
    
    Node(int x, int y, int steps) : x(x), y(y), steps(steps) {}
};

// 方向数组:右、左、下、上
int dx[] = {0, 0, 1, -1};
int dy[] = {1, -1, 0, 0};

// BFS求解最短路径
int bfs(int startX, int startY) {
    queue<Node> q;
    q.push(Node(startX, startY, 0));
    maze[startX][startY] = 1; // 标记起点已访问

    while (!q.empty()) {
        Node current = q.front();
        q.pop();

        // 到达终点
        if (current.x == n && current.y == m) {
            return current.steps;
        }

        // 尝试四个方向
        for (int i = 0; i < 4; i++) {
            int nx = current.x + dx[i];
            int ny = current.y + dy[i];

            // 检查新位置是否合法
            if (nx < 1 || ny < 1 || nx > n || ny > m || maze[nx][ny]) {
                continue;
            }

            maze[nx][ny] = 1; // 标记已访问
            q.push(Node(nx, ny, current.steps + 1));
        }
    }

    return -1; // 如果没有找到路径
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n >> m;
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> maze[i][j];
        }
    }

    cout << bfs(1, 1) << endl;
    return 0;
}

3.2 核心优化技巧

  1. 即时标记:在入队时立即标记已访问,避免重复访问
  2. 提前终止:找到终点立即返回
  3. 边界检查:防止数组越界

四、复杂度分析与优化方向

4.1 时间复杂度

  • 最坏情况: O ( n ∗ m ) O(n*m) O(nm)

4.2 空间复杂度

  • O ( n ∗ m ) O(n*m) O(nm)(队列最大长度)

4.3 进阶优化思路

  1. 双向BFS:从起点和终点同时搜索
  2. A*算法:结合启发式搜索
  3. 分层BFS:减少空间占用

五、BFS的广泛应用场景

  1. 最短路径:无权图的最短路径
  2. 连通性检测:判断图的连通性
  3. 状态空间搜索:解决八数码等问题
  4. 社交网络:计算人际关系距离

结语:算法之美的永恒追求

通过走迷宫问题,我们不仅掌握了BFS的精髓,更体会到算法设计中"简单与高效"的完美统一。从古老的迷宫谜题到现代的网络路由,BFS算法始终闪耀着智慧的光芒。当您下次面对复杂问题时,不妨想想这个简单的队列,它或许就是打开问题之门的钥匙。


思考题:如何修改算法使其可以输出具体的最短路径?
tips:试着维护一个 p p p数组,记录每一个走过的节点的父节点试试呢)


http://www.kler.cn/a/536157.html

相关文章:

  • 深入理解linux中的文件(下)
  • 【AI大模型】Ubuntu18.04安装deepseek-r1模型+服务器部署+内网访问
  • 策略模式(Strategy)
  • 通过多层混合MTL结构提升股票市场预测的准确性,R²最高为0.98
  • 解锁反序列化漏洞:从原理到防护的安全指南
  • RFID隧道机:提升生产流水线效率与精准度
  • 【JS】element-ui table展示勾选状态
  • AI工具——Cherry Studio,搭建满血DeepSeek R1的AI对话客户端
  • 【医院绩效管理专题】2.绩效管理:医院发展的核心驱动力
  • Jmeter接口自动化测试
  • ZIP完美解密解压缩和暴力破解最佳实现
  • python图片转字符画应用
  • Java 集合中的 `removeIf` 和 Stream API 的 `filter`
  • 4.Python字符串和列表:字符串输入、字符串输出、下标和切片、字符串常见函数、列表(list)、列表的循环遍历、列表的增删改查、列表的嵌套、列表的切片
  • 基于单片机的电子抢答器设计(论文+源码+实物)
  • Vue 3 30天精进之旅:Day 17 - 样式和动画
  • UE学习日志#24 C++笔记#10 内存管理1
  • linux——网络计算机{序列化及反序列化(JSON)(ifdef的用法)}
  • DeepSeek本地化部署
  • 【实战】excel分页写入导出大文件
  • 如何在Android Studio中开发一个简单的Android应用?
  • 【截图】selenium自动通过浏览器截取指定元素div的图片
  • 深度剖析 Redis:缓存穿透、击穿与雪崩问题及实战解决方案
  • java将list转成树结构
  • 劳务报酬所得税
  • DeepSeek辅助段落扩写的能力怎么样?