算法笔记8.2节给了这样一个例题:
给出一个m*n的矩阵,矩阵中的元素为0或1.称位置(x,y)与其上下左右四个位置是相邻的。如果矩阵中有若干个1是相邻的(不必两两相邻),那么称这些1构成了一个“块”。求给定的矩阵中“块”的个数。
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 |
例如上面的6*7的矩阵中,块的个数为4。
在算法笔记中给出的BFS解法如下:
/*------------BFS---------------*/
#include<cstdio>
#include<iostream>
#include<queue>
using namespace std;
const int maxn=100;
struct node{
int x,y;
}Node;
int n,m;//矩阵大小
int matrix[maxn][maxn];//01矩阵
bool inq[maxn][maxn]={false};//记录位置(x,y)是否被记录过
int X[4]={0,0,1,-1};
int Y[4]={1,-1,0,0};
/*判断点(x,y)是否被访问过*/
bool judge(int x,int y){
if(x>=n||x<0||y>=m||y<0)return false;
if(matrix[x][y]==0||inq[x][y]==true)return false;
return true;
}
void BFS(int x,int y){
queue<node> Q;
Node.x=x,Node.y=y;
Q.push(Node);
inq[x][y]=true;//记录该点已经被访问过
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)){
//如果点(newX,newY)没有被访问过,那么将该点入队并且标记为已经被访问过
Node.x=newX,Node.y=newY;
Q.push(Node);
inq[newX][newY]=true;
}
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
scanf("%d",&matrix[i][j]);
}
}
int ans=0;//记录答案块数
for(int x=0;x<n;x++){
for(int y=0;y<m;y++){
if(matrix[x][y]==1&&inq[x][y]!=1){
//如果该点满足要求而且尚未被访问过,那么块数ans++并且执行BFS
ans++;
BFS(x,y);
}
}
}
printf("%d\n",ans);
return 0;
}
然后根据这个解法,我改写了一个用DFS实现的算法:
/*---------DFS---------*/
bool end(int x,int y){
for(int i=0;i<4;i++){
if(matrix[x+X[i]][y+Y[i]]==1&&inq[x+X[i]][y+Y[i]]==false){
return false;
}
}
return true;
}
void DFS(int x,int y){
inq[x][y]=true;
if(end(x,y))return;
for(int i=0;i<4;i++){
if(judge(x+X[i],y+Y[i])){
DFS(x+X[i],y+Y[i]);
}
}
}
用递归实现DFS,因为要有递归边界,我的想法是当正在访问的这个点的上下左右为1的点都被访问过的时候就是递归边界,所以我写了一个end函数,当正在访问的这个点的上下左右为1的点只要出现了有一个点没被访问过,就返回false,意味着没有到递归边界,应当继续执行DFS,否则end函数就返回true,意味着到达递归边界,return。不知道我这样的想法是对还是错,欢迎看到这篇博客的人能提一些建议。
其实通过这个例子,我更加充分地更深入地理解了DFS和BFS的思想,也加深了我对递归和分治的理解。