广度优先遍历就是从某个顶点出发,首先访问这个顶点,然后找出这个结点的所有未被访问的邻接点,访问完后再访问这些结点中第一个邻接点的所有结点,重复此方法,直到所有结点都被访问完为止。
个人总结:
广度优先搜索方法具有以下特点:
- 依次访问该层所有节点
- 使用队列保存符合条件的节点
- 条件定义如下:
- 该节点并未访问过(并未加入过队列,可以使用布尔类型矩阵保存节点的访问状态)
- 该节点满足搜索的要求(如搜索矩阵中相邻的1等)
- 截止条件一般为:保存节点的队列不为空
- 使用BFS搜索方法找到的解一般可以作为最优解(一层一层向下搜索,故首先满足条件的解一定是最优解)
- 广度优先搜索方法一般使用循环+队列实现,并不涉及递归
BFS典型例题——矩阵的搜索
例题: 给出一个m x n 的矩阵,矩阵中的元素为0或1。称位置(x, y) 与其上下左右四个位置(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)是相邻的。那么称这些1构成了一个块。求给定的矩阵中块的个数。
0111001
0010000
0000100
0001110
1110100
1111000
例如上面6x7的矩阵中,块的个数为4.
求解思路:
首先,面对上下左右四个位置的搜索,我们可以使用向量来表示
int X[4] = {1, -1, 0, 0}, Y[4] = {0, 0, 1, -1}; //分别表示左右上下
其次,根据题意,我们可以总结出筛选节点的条件:
节点值为1且未加入过队列(节点在矩阵内)
代码实现如下:
bool judge(int x, int y){
if(x < 0 || y < 0 || x >=m || y >=n){
return false; // 该分支表示节点的坐标不在矩阵内
}else if(area[x][y] == 0 || reached[x][y]){
return false; // 该分支表示节点的值为1或者节点已经加入过队列
}else{
return true; // 该分支表示节点满足值为1且未加入过队列的条件
}
}
在知道搜索的筛选条件之后,我们可以按照流程设计BFS搜索:
void BFS(int x, int y){
queue<node> q;
// 初始节点
Node.x = x;
Node.y = y;
reached[x][y] = true;
q.push(Node);
// 根据流程开始BFS搜索
while(!q.empty()){
// 1. 获得队首节点
node top = q.front();
// 2. 将队首节点出列
q.pop();
// 3. 遍历与队首节点相邻的节点,将符合筛选条件的节点加入队列
for(int i = 0; i < 4; i++){
int new_x = top.x + X[i], new_y = top.y + Y[i]; // 队首节点的左、右、上、下节点
if(judge(new_x, new_y)){
// 节点通过筛选条件,加入队列,此外还要更改该节点的访问状态,避免重复加入队列
Node.x = new_x;
Node.y = new_y;
q.push(Node);
reached[new_x][new_y] = true;
}
}
}
}
当设计出BFS之后,我们可以开始设计我们的主流程,思路如下:
- 遍历矩阵中每一个节点
- 对符合条件的节点进行BFS(一次BFS能够将与该节点相邻的所有节点找到,可以理解为连通分量)
- 块的数目加一
代码实现如下:
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(judge(area[i][j])){
ans += 1;
BFS(i, j);
}
}
}
2021.8.29 计算机201 叶俊