岛屿个数(DFS+BFS)
1.岛屿个数 - 蓝桥云课 (lanqiao.cn)
**法一:**本题很妙啊,你发现没有。反向操作,不是直接算岛屿数量,而是先将最外层的海洋给标注出来(DFS,visit[i][j]=2
),然后再用一次DFS把所有visit[i][j]=0
的当做整体,这些不管里面有没有子岛屿,有多少个子岛屿,都是一个大岛屿。
本题你可以学到的:标记海洋(八方、只遍历最外面一圈)、标记岛屿(四方、无回溯(因为我们要全部标记,不是找路径))
**法二:**这里提供一个思路,在评论区看到另一个大佬写的:搜索出所有岛屿,这个不难做到。由于岛屿之间互相隔离,则如果岛屿的一个格子在一个环内,那么整个岛屿也都在环内。遍历所有的岛屿,选中当前岛屿的第一个格子,搜索周围海洋,若能搜索到地图的边界外,则此岛屿不在任何一个环内;否则,此岛屿在某个环内,岛屿数量减一。
这个思路和我做的方法区别在于,我是从外向内,而该思路是正向思考,然后利用内岛的性质,对整体做出进一步加工。
下面给出法一的代码:
AC:DFS
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
using namespace std;
//牛的,我是真的没想到,可以反向操作,不好求里面,那就求外面,把外层海水搜素出来,
// 这样剩下的不管有没有內岛屿,都是一个岛屿 他们用的是bfs,我这里用dfs试试,应该也可以
//注意,检索海水和检索岛屿不一样,海水是八方搜索,而岛屿是四方搜素const int N = 105;
int t, n, m; //m行 n列
int nx[4] = { 0,1,0,-1 };
int ny[4] = { 1,0,-1,0 };
int seax[8] = { 0,1,1,1,0,-1,-1,-1 };
int seay[8] = { 1,1,0,-1,-1,-1,0,1 };
vector<string>g;
int visit[N][N] = { 0 };
int landNum = 0;void dfs(int x, int y)
{for (int i = 0; i < 8; i++){int xx = x + seax[i];int yy = y + seay[i];if (xx < 0 || xx >= m || yy < 0 || yy >= n) continue; //越界if (g[xx][yy] == '0' && visit[xx][yy] == 0){visit[xx][yy] = 2;dfs(xx, yy);//visit[xx][yy] = 0; //回溯}}
}void dfsLand(int x, int y)
{for (int i = 0; i < 4; i++){int xx = x + nx[i];int yy = y + ny[i];if (xx < 0 || xx >= m || yy < 0 || yy >= n) continue; //越界if (visit[xx][yy] == 0) //接下来就和g的值无关了{visit[xx][yy] = 1;dfsLand(xx, yy);}}
}int main()
{cin >> t;while (t--){g.clear();memset(visit, 0, sizeof(visit));landNum = 0;cin >> m >> n;getchar();string str;for (int i = 0; i < m; i++){getline(cin, str);g.push_back(str);}//检索海洋//for (int i = 0; i < n; i++)//{// for (int j = 0; j < m; j++)// {// if (g[i][j] == '0' && visit[i][j] == 0) // {// visit[i][j] = 2; //最外层海水 标记为2// dfs(i,j);// }// }//} //这样标记海水不是最外层的,而是所有//要标记外海,其实只需要遍历最外面的一圈for (int i = 0; i < m; i++){for (int j = 0; j < n; j++){if (i == 0 || j == 0 || i == m - 1 || j == n - 1){if (g[i][j] == '0' && visit[i][j] == 0){visit[i][j] = 2; //最外层海水 标记为2dfs(i, j);}}}}//检索岛屿for (int i = 0; i < m; i++){for (int j = 0; j < n; j++){if (visit[i][j] == 0) //剩下的都是岛屿{landNum++;visit[i][j] = 1; //陆地 标记为1dfsLand(i, j);}}}cout << landNum << endl;}return 0;
}
二刷:思路和一刷差不多,这里用的BFS,主要是代码方面有些许差别大家可以选择容易理解的。
BFS:
//岛屿个数
#include <iostream>
#include <cstring>
#include <queue>
#include <string>
using namespace std;//思路:正难则反,我们先不统计岛屿的个数,先将所有的外海标记,那么剩下的没有标记的就是岛屿
//注意,海水是8方走 const int N=53;
int g[N][N]={0}; //0海 1陆
int book[N][N]={0}; //1表示已经遍历过
int n,m,k;
int mx[8]={-1,-1,0,1,1,1,0,-1};
int my[8]={0,1,1,1,0,-1,-1,-1};
int imx[4]={0,0,1,-1};
int imy[4]={1,-1,0,0};struct Pos
{int x,y;
};bool isOut(int x,int y)
{if(x<0 || x>=m || y<0 || y>=n) return true; //确实出界了if(g[x][y]==1) return true; //陆地,不能走return false; //没有出界
}bool islandOut(int x,int y)
{if(x<0 || x>=m || y<0 || y>=n) return true; //确实出界了//注意这里不需要再判断是否为海洋了,因为我们将这块岛屿内海也视为岛屿 return false; //没有出界
}void bfs_island(int x,int y)
{queue<Pos>q;q.push({x,y});book[x][y]=1; while(!q.empty()){Pos cur=q.front();q.pop();for(int i=0;i<4;i++){Pos nex;nex.x=imx[i]+cur.x;nex.y=imy[i]+cur.y;if(islandOut(nex.x,nex.y) || book[nex.x][nex.y]==1) continue;book[nex.x][nex.y]=1; q.push(nex);}}
}void bfs_sea(int x,int y)
{queue<Pos>q;q.push({x,y});book[x][y]=1;while(!q.empty()){Pos cur=q.front();q.pop();for(int i=0;i<8;i++){Pos nex;nex.x=mx[i]+cur.x;nex.y=my[i]+cur.y;if(isOut(nex.x,nex.y) || book[nex.x][nex.y]==1) continue;book[nex.x][nex.y]=1; q.push(nex);}}
}int main()
{cin>>k;while(k--){cin>>m>>n;int ans=0;memset(book,0,sizeof(book));memset(g,0,sizeof(g));string str;for(int i=0;i<m;i++){cin>>str;for(int j=0;j<n;j++){if(str[j]=='1')g[i][j]=1;}}for(int i=0;i<n;i++) //第一行 if(g[0][i]==0 && book[0][i]==0)bfs_sea(0,i);for(int i=0;i<m;i++) //第一列 if(g[i][0]==0 && book[i][0]==0)bfs_sea(i,0); for(int i=0;i<n;i++) //第m-1行 if(g[m-1][i]==0 && book[m-1][i]==0)bfs_sea(m-1,i);for(int i=0;i<m;i++) //第n-1列 if(g[i][n-1]==0 && book[i][n-1]==0)bfs_sea(i,n-1);for(int i=0;i<m;i++)for(int j=0;j<n;j++){if(book[i][j]==0) //没标记的,就是岛屿{ans++;bfs_island(i,j); } }cout<<ans<<endl;}return 0;}