c++开发进阶之广度优先搜索(BFS)

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

1. 广度优先搜索(BFS)的基本概念

广度优先搜索(BFS, Breadth First Search)是一种遍历或搜索图(Graph)和树(Tree)的算法。BFS 从某个起点开始,优先访问距离起点最近的节点,然后逐步扩展到距离更远的节点。BFS 采用队列(Queue)来保持遍历过程中的待访问节点,确保节点按照距离起点的层次顺序进行访问。

2. BFS 的工作原理

BFS 的基本思想是:

  • 从起始节点开始,将它标记为已访问并放入队列。
  • 不断从队列中取出节点,访问它的所有邻接节点,并将未访问过的邻接节点加入队列。
  • 重复上述步骤,直到队列为空。

BFS 是一种无回溯的搜索算法,因为它总是优先访问当前层次的所有节点,再进入下一层次。

3. BFS 的应用场景
  • 最短路径:BFS 可以用于无权图中寻找最短路径,因为它总是按层次遍历节点。
  • 连通性检测:BFS 可以用于检测图是否连通。
  • 层次遍历:可以用于二叉树或其他树结构的层次遍历。
  • 拓扑排序:BFS 可以用于有向无环图(DAG)的拓扑排序。
4. BFS 的复杂度分析
  • 时间复杂度:O(V + E),其中 V 是节点数,E 是边数。每个节点和每条边都最多被访问一次。
  • 空间复杂度:O(V),需要存储访问标记数组和队列。
5. BFS 的实现

BFS 通常使用队列来实现,以下是 C++ 中 BFS 的代码实现。

(1) 图的 BFS 实现
#include <iostream>
#include <vector>
#include <queue>

using namespace std;

void BFS(const vector<vector<int>>& vvGraph, int iStartNode)
{
    int iTotalNodes = vvGraph.size();

    // 创建访问标记数组,初始化为false,表示所有节点尚未被访问
    vector<bool> vbVisited(iTotalNodes, false);

    // 创建队列,用于BFS遍历
    queue<int> qNodes;
    
    // 将起始节点标记为已访问,并加入队列
    vbVisited[iStartNode] = true;
    qNodes.push(iStartNode);

    while(!qNodes.empty())
    {
        // 取出队列头部节点并访问
        int iCurrentNode = qNodes.front();
        qNodes.pop();
        cout << iCurrentNode << " ";

        // 遍历当前节点的所有邻接节点
        for(int iNeighbor : vvGraph[iCurrentNode])
        {
            // 如果邻接节点尚未访问,则标记为已访问并加入队列
            if(!vbVisited[iNeighbor])
            {
                vbVisited[iNeighbor] = true;
                qNodes.push(iNeighbor);
            }
        }
    }
}

int main()
{
    // 创建图的邻接表
    vector<vector<int>> vvGraph = {
        {1, 2},      // 节点 0 与节点 1、2 相连
        {0, 2, 3},   // 节点 1 与节点 0、2、3 相连
        {0, 1, 4},   // 节点 2 与节点 0、1、4 相连
        {1, 5},      // 节点 3 与节点 1、5 相连
        {2},         // 节点 4 与节点 2 相连
        {3}          // 节点 5 与节点 3 相连
    };

    // 从节点 0 开始执行 BFS
    cout << "BFS starting from node 0: ";
    BFS(vvGraph, 0);

    return 0;
}

输出结果:

BFS starting from node 0: 0 1 2 3 4 5

代码详解:

  1. 队列 qNodes:用于存储当前层次的节点,当队列非空时,继续遍历。
  2. 访问标记数组 vbVisited:用于防止重复访问节点,确保每个节点最多被访问一次。
  3. 邻接表 vvGraph:图通过邻接表来表示,其中 vvGraph[i] 表示节点 i 的所有邻居。
  4. 队列操作:从队列中取出一个节点,将其所有未访问的邻居加入队列,并标记为已访问。
(2) BFS 在二维网格中的应用

BFS 可以用于在二维网格(如迷宫)中寻找从起点到终点的最短路径。以下是一个 BFS 应用于二维网格的示例,假设我们在一个迷宫中寻找最短路径。

#include <iostream>
#include <queue>
#include <vector>

using namespace std;

// 表示一个位置的结构体
struct Position
{
    int iRow;
    int iCol;
};

// 定义移动的四个方向:上、下、左、右
vector<Position> directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};

// 判断是否可以移动到目标位置
bool isValid(int iRow, int iCol, int iTotalRows, int iTotalCols, const vector<vector<int>>& grid, const vector<vector<bool>>& vbVisited)
{
    return iRow >= 0 && iRow < iTotalRows && iCol >= 0 && iCol < iTotalCols && !vbVisited[iRow][iCol] && grid[iRow][iCol] == 0;
}

// 使用 BFS 找到从起点到终点的最短路径
int BFSGrid(const vector<vector<int>>& grid, Position start, Position end)
{
    int iTotalRows = grid.size();
    int iTotalCols = grid[0].size();

    // 创建访问标记数组
    vector<vector<bool>> vbVisited(iTotalRows, vector<bool>(iTotalCols, false));

    // 创建队列,用于BFS遍历
    queue<pair<Position, int>> qNodes;  // 保存当前位置和路径长度
    qNodes.push({start, 0});
    vbVisited[start.iRow][start.iCol] = true;

    while(!qNodes.empty())
    {
        // 取出队列头部节点
        auto current = qNodes.front();
        Position currentPosition = current.first;
        int iCurrentDistance = current.second;
        qNodes.pop();

        // 如果到达终点,返回路径长度
        if(currentPosition.iRow == end.iRow && currentPosition.iCol == end.iCol)
        {
            return iCurrentDistance;
        }

        // 遍历四个方向
        for(const auto& direction : directions)
        {
            int iNewRow = currentPosition.iRow + direction.iRow;
            int iNewCol = currentPosition.iCol + direction.iCol;

            // 如果该位置可以访问,则加入队列
            if(isValid(iNewRow, iNewCol, iTotalRows, iTotalCols, grid, vbVisited))
            {
                vbVisited[iNewRow][iNewCol] = true;
                qNodes.push({{iNewRow, iNewCol}, iCurrentDistance + 1});
            }
        }
    }

    // 如果无法到达终点,返回 -1
    return -1;
}

int main()
{
    // 定义迷宫:0 表示通路,1 表示障碍
    vector<vector<int>> grid = {
        {0, 0, 1, 0, 0},
        {0, 0, 0, 0, 1},
        {1, 1, 0, 1, 0},
        {0, 0, 0, 1, 0},
        {0, 1, 0, 0, 0}
    };

    // 定义起点和终点
    Position start = {0, 0};
    Position end = {4, 4};

    // 执行BFS寻找最短路径
    int iShortestPath = BFSGrid(grid, start, end);

    if(iShortestPath != -1)
    {
        cout << "The shortest path length is: " << iShortestPath << endl;
    }
    else
    {
        cout << "There is no path to the destination." << endl;
    }

    return 0;
}

输出结果:

The shortest path length is: 8

代码详解:

  1. Position 结构体:用于表示二维网格中的位置(行和列)。
  2. directions:定义了四个方向:上、下、左、右,用于遍历相邻的网格单元。
  3. isValid 函数:检查新位置是否在网格范围内,未被访问过且是通路(即值为 0)。
  4. BFSGrid 函数:使用 BFS 遍历二维网格,从起点到终点,找到最短路径。
  5. queue<pair<Position, int>>:队列存储当前的位置和从起点到当前位置的路径长度。
6. BFS 与 DFS 的区别
  • 遍历顺序:BFS 按层次遍历(即先访问所有距离起点为 1 的节点,然后是距离为 2 的节点,以此类推),而 DFS 则尽可能深入探索节点。
  • 最短路径:BFS 在无权图中可以保证找到最短路径,而 DFS 不一定适合找最短路径。
  • 数据结构:BFS 采用队列,而 DFS 采用递归或栈。
7. 总结

BFS 是一种非常重要的图遍历算法,适用于寻找无权图的最短路径、层次遍历等应用。通过队列的机制,它能够高效地按层次逐步遍历图中的所有节点,是解决多种问题的基础算法之一。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值