c++开发进阶之广度优先搜索(BFS)
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
代码详解:
- 队列
qNodes
:用于存储当前层次的节点,当队列非空时,继续遍历。 - 访问标记数组
vbVisited
:用于防止重复访问节点,确保每个节点最多被访问一次。 - 邻接表
vvGraph
:图通过邻接表来表示,其中vvGraph[i]
表示节点i
的所有邻居。 - 队列操作:从队列中取出一个节点,将其所有未访问的邻居加入队列,并标记为已访问。
(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
代码详解:
Position
结构体:用于表示二维网格中的位置(行和列)。directions
:定义了四个方向:上、下、左、右,用于遍历相邻的网格单元。isValid
函数:检查新位置是否在网格范围内,未被访问过且是通路(即值为 0)。BFSGrid
函数:使用 BFS 遍历二维网格,从起点到终点,找到最短路径。queue<pair<Position, int>>
:队列存储当前的位置和从起点到当前位置的路径长度。
6. BFS 与 DFS 的区别
- 遍历顺序:BFS 按层次遍历(即先访问所有距离起点为 1 的节点,然后是距离为 2 的节点,以此类推),而 DFS 则尽可能深入探索节点。
- 最短路径:BFS 在无权图中可以保证找到最短路径,而 DFS 不一定适合找最短路径。
- 数据结构:BFS 采用队列,而 DFS 采用递归或栈。
7. 总结
BFS 是一种非常重要的图遍历算法,适用于寻找无权图的最短路径、层次遍历等应用。通过队列的机制,它能够高效地按层次逐步遍历图中的所有节点,是解决多种问题的基础算法之一。
更多推荐
已为社区贡献8条内容
所有评论(0)