一、概述
输入一个二维数组,元素均为0或1,0表示陆地,1表示海洋,返回孤岛个数。孤岛定义为一连串的陆地,其上下左右均为海洋。孤岛不能与二维数组边缘相连。
服务器搞得我心态爆炸。本来这题可以提交的——那样我就1360/6058,而不是现在的1925/6058,前进600名呢。结果十一点五十开始连不上,气得我差点把电脑砸了。
二、分析
这个题第一眼我看到的时候是懵逼的:
什么叫孤岛啊?怎么判断孤岛啊?
然后发现了孤岛的特征:它是一坨连起来的0,周围全是1,注意只要上下左右四面,不用八方。
然后继续归纳,我挨着边缘,但三面环水的算不算啊?不算。只有严格四面环水的才算。
那么问题清楚了一些:和边缘挨着的叫陆地,陆地都不算。
我们可以先把陆地都找出来,再在其余的地方找孤岛。
怎么找陆地呢?这是个问题。可以先在二维数组,我们叫它地图吧,在地图最外圈走一圈,把所有的0标记为陆地,然后再去内部,和陆地相连的也是陆地。这样做。
如何实现“和陆地相连的也是陆地”?用DFS。DFS从一块陆地出发,之后只要遇到0,就标记为陆地,遇到1就返回。这点很重要。我们称一次DFS为一次污染。
要污染多少次呢?我们绕着地图最外面走一圈,碰到陆地,我们把它标记为-1吧,就开始污染。走完了也就污染完了。
现在看地图,其中有三种元素:-1:陆地;0:岛屿;1:海水。然后开始找岛屿。
找岛屿的方法和找陆地一样:我们这次要遍历一整张地图,遇到岛屿就开始污染,将其标记为t,每次污染完一个岛屿,t递增1。什么叫污染完岛屿呢?DFS完一次啊。当我们遍历完整张地图,那么就将所有的岛屿标记为t、t+1、t+2等等。
返回t即可。
这里有一个技巧,我们将t赋值为10,以和0,1,-1区分开。然后返回t-10即可。
代码如下:
class Solution {
void DFS(vector<vector<int>>& g,int i,int j,int t)
{
if(i<g.size()&&j<g[0].size()&&g[i][j]==0)
{
g[i][j]=t;
DFS(g,i,j+1,t);
DFS(g,i+1,j,t);
DFS(g,i,j-1,t);
DFS(g,i-1,j,t);
}
}
void DFS1(vector<vector<int>>& g,int i,int j,int t)
{
if(i<g.size()&&j<g[0].size()&&g[i][j]<=0)
{
g[i][j]=t;
DFS(g,i,j+1,t);
DFS(g,i+1,j,t);
DFS(g,i,j-1,t);
DFS(g,i-1,j,t);
}
}
public:
int closedIsland(vector<vector<int>>& g) {
int res=10;
for(int i=0;i<g[0].size();i++)
{
if(g[0][i]==0)
g[0][i]=-1;
if(g[g.size()-1][i]==0)
g[g.size()-1][i]=-1;
}
for(int i=0;i<g.size();i++)
{
if(g[i][0]==0)
g[i][0]=-1;
if(g[i][g[0].size()-1]==0)
g[i][g[0].size()-1]=-1;
}//标记地图周边的陆地
for(int i=0;i<g.size();i++)
for(int j=0;j<g[0].size();j++)
{
if(g[i][j]==-1)
DFS1(g,i,j,-1);
}//污染所有陆地
for(int i=1;i<g.size()-1;i++)
{
for(int j=1;j<g[0].size()-1;j++)
{
if(g[i][j]==0)
{
DFS(g,i,j,res);
res++;//污染岛屿
}
}
}
return res-10;
}
};
三、总结
这种题目第一次做,主要是思维的转换——能够找到解已经很好了,不要去奢求时间复杂度多么好。也学到了用DFS来寻找二维数组中被包围的块的方法。