广度优先搜索(BFS)
引入
如果说深度优先搜索是“不撞南墙不回头”,那么广度优先搜索就是“剥洋葱”。
因为洋葱是一层一层的。
算法模板
这个从树的角度来看,就是层次遍历(一层一层的,从上到下,从左到右)
创建一个队列
先将根节点入队
while(队列非空){
获取头结点;
访问头结点;
头结点出队;
基于当前的结点,遍历所有与之相邻,且未入队的所有结点,将其入队
}
这个需要注意是将所有“未入队”的结点入队,而非“未访问”。
未入队,就是从来没进过队列
未访问,则包含了已经进队,当时还没排到访问(会重复入队)
例子
给出一个 m×n 的矩阵,矩阵中的元素为 0 或 1。称位置 (x, y) 与其上、下、左、右四个位置是相邻的。如果矩阵中有若干个 1 是相邻的(不必考虑两两相邻),那么称这些 1 构成了一个“块”。求给定矩阵中“块”的个数。
Sample Input:
6 7
0 1 1 1 0 0 1
0 0 1 0 0 0 0
0 0 0 0 1 0 0
0 0 0 1 1 1 0
1 1 1 0 1 0 0
1 1 1 1 0 0 0
Sample Output:
4
思路:
对于矩阵 Matrix[m][n] 遍历每个元素,如果为0,则跳过;如果为 1 ,则使用BFS查询与该位置相邻的4个位置(前提是不出界),判断它们是否为 1(如果相邻的位置为1,则同样去查询与该位置相邻的4个位置,直到整个“1”块访问完毕)。为了防止走回头路,设置一个 bool型的 inq数组记录 每个元素是否已被访问过(在BFS中入队)。
技巧:
对于当前元素的下一层:上下左右,可以用增量数组访问,设置2个增量数组,来表示4个方向。
int X[] = {-1, 1, 0, 0}; // 上下左右
int Y[] = {0, 0, -1, 1};
代码
#include<stdio.h>
#include<queue>
using namespace std;
const int maxn = 100;
int m,n;//m行n列
int mar[maxn][maxn];
struct Node {
int x;
int y;
} node;
//标记四个方向
int X[4] = {0,0,-1,1};
int Y[4] = {1,-1,0,0};
//是否入队判断
bool inq[maxn][maxn] = {false};
bool judge(int x,int y) {
if(x<=0||x>m||y<=0||y>n)//越界判断
return false;
if(inq[x][y] == true||mar[x][y] == 0)
return false;
return true;
}
void BFS(int x,int y) {
queue<Node> Q;
node.x = x;
node.y = y;
Q.push(node);
while(!Q.empty()) {
Node top = Q.front();
Q.pop();
for(int i =0; i<4; i++) {
int newx = top.x+X[i];
int newy = top.y + Y[i];
if(judge(newx,newy)) {
node.x = newx;
node.y = newy;
Q.push(node);
inq[newx][newy] = true;
}
}
}
}
int main() {
scanf("%d %d",&m,&n);
for(int i = 1; i<=m; i++)
for(int j = 1; j<=n; j++)
scanf("%d",&mar[i][j]);
int ans = 0;
for(int i = 1; i<=m; i++)
for(int j = 1; j<=n; j++) {
if(inq[i][j] == false && mar[i][j] == 1) {
ans++;
BFS(i,j);
}
}
printf("%d\n",ans);
return 0;
}