题目描述:
In a 2D grid of 0
s and 1
s, we change at most one 0
to a 1
.
After, what is the size of the largest island? (An island is a 4-directionally connected group of 1
s).
Example 1:
Input: [[1, 0], [0, 1]]
Output: 3
Explanation: Change one 0 to 1 and connect two 1s, then we get an island with area = 3.
Example 2:
Input: [[1, 1], [1, 0]]
Output: 4
Explanation: Change the 0 to 1 and make the island bigger, only one island with area = 1.
Example 3:
Input: [[1, 1], [1, 1]]
Output: 4
Explanation: Can't change any 0 to 1, only one island with area = 1.
解法一:DFS-深度优先搜索(The Time Exceeded)
这道题很容易想到的且很暴力的一种解法是DFS。我们对二维vector里的每一个‘0’处理(假设其坐标为(i,j))。首先将其变为‘1’,然后以其坐标为起点使用DFS算法,遍历其4-连接点并对每点调用递归获得该区块的大小,最后将其还原为0。我们需要维持一个当前所有区块面积的最大值。当遍历完所有的值为‘0’的点后,我们所维持的这个便是结果。此外,我们需要一个与输入grid同等大小的二维bool型vector来标志在当次DFS中,grid里的每个元素是否已经被访问过,每次DFS后将其用temp归零。
具体的DFS算法命名为calculate,接收坐标点(i,j)及grid,visit。当i,j在grid范围内时返回计数+1并递归坐标的4-连接点。
bool变量‘haszero’标志grid中是否有0,没有的话直接返回grid中1的个数。
bool hasone函数是对计算量的一个优化,如过我们遍历所有0会加大计算量。我们先判断一个位置的0周围是否有1,有的话再对其调用DFS函数。
但遗憾的是,这种C++方法被判定超时,因为每次对0进行遍历且进行DFS,最差情况的代码时间复杂度为O((MN)^2)。但是相同方法的JAVA代码却可以通过。
class Solution {
public:
int largestIsland(vector<vector<int>>& grid){
int m=grid.size();
int n=grid[0].size();
int result=0;
bool haszero=0;
if(grid==vector<vector<int>>(n,vector<int>(m,0))) return 1;
vector<vector<bool>>visit(n,vector<bool>(m,false));
vector<vector<bool>>temp(n,vector<bool>(m,false));
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]==0){
if(hasone(i,j,grid)==true){
grid[i][j]=1;
result=max(result,calculate(i,j,grid,visit));
if(result==m*n) return result;
grid[i][j]=0;
visit=temp;
}
haszero=1;
}
}
}
return haszero?result:m*n;
}
int calculate(int i,int j,vector<vector<int>> grid,vector<vector<bool>> &visit){
if(i < 0 || j < 0 || i >= grid.size() || j >= grid[0].size() || grid[i][j] == 0||visit[i][j]) return 0;
visit[i][j]=true;
int curSum=1+calculate(i+1,j,grid,visit)+calculate(i-1,j,grid,visit)+calculate(i,j-1,grid,visit)+calculate(i,j+1,grid,visit);
return curSum;
}
bool hasone(int i,int j,vector<vector<int>> grid){
if(!(i < 0 || j < 0 || (i +1)>= grid.size() || j >= grid[0].size() || grid[i+1][j] == 0)) return true;
if(!((i -1)< 0 || j < 0 || i>= grid.size() || j >= grid[0].size() || grid[i-1][j] == 0)) return true;
if(!(i < 0 || j < 0 || (i)>= grid.size() || (j+1) >= grid[0].size() || grid[i][j+1] == 0)) return true;
if(!(i < 0 || (j-1) < 0 || (i)>= grid.size() || j >= grid[0].size() || grid[i][j-1] == 0)) return true;
return false;
}
};
解法二:二维数组分块技术
解法一之所以不通过,是因为每次遍历0的时候都需要对grid进行一次DFS,实际上我们会对一个‘1’块多次重复计算。那么如果我们先把每个块的大小计算(函数dfs),把块中的每个像素用相同的值(value,初值设为2以和原grid的0与1做区分,每次循环后+1)标定出来,同时,不同的块的标定值这不一样,它们是colourful的。这里我们要用到一个名为lands的unordered_map将不同颜色的块和该块的大小进行一一映射。后再遍历grid中的每个0位置上的点的4方向上的块的大小(函数connectLand,同时要注意不要加到有相同颜色的块)并取维持一个其的最大值即为结果。如下图(图片来自leetcode 827 Discuss里的votrubac):
在DFS因为已经被visit的‘1’会被赋值为彩色,所以我们不需要像解法一那样创建一个visit数组。在connectLand函数里的返回值要+1以表示将我们要改变的‘0’坐标变为‘1’后增加的总面积。
这样算法的时间复杂度就降低到了O(N*M),得以通过。
class Solution {
public:
int m=0;
int n=0;
unordered_map<int,int>lands;
int largestIsland(vector<vector<int>>& grid){
m=grid.size();
n=grid[0].size();
int value=2;
int result=0;
bool haszero=0;
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
if(grid[i][j]==1){
int curSum=0;
dfs(i,j,value,grid,curSum);
lands[value]=curSum;
value++;
}
}
}
for(int i=0;i<m;i++){
for(int j=0;j<m;j++){
if(grid[i][j]==0){
haszero=1;
result=max(result,connectLand(i,j,grid));
}
}
}
return haszero?result:m*n;
}
void dfs(int i,int j,int value,vector<vector<int>>& grid,int &curSum){
if(i<0||i>=grid.size()||j<0||j>=grid[0].size()) return;
if(grid[i][j]!=1) return;
if(grid[i][j]==1){
grid[i][j]=value;
curSum++;
}
dfs(i+1,j,value,grid,curSum);
dfs(i-1,j,value,grid,curSum);
dfs(i,j+1,value,grid,curSum);
dfs(i,j-1,value,grid,curSum);
return;
}
int connectLand(int i,int j,vector<vector<int>> grid){
int sum=0;
int flag1=0,flag2=0,flag3=0;
if((i+1)>=0&&(i+1)<grid.size()&&j>=0&&j<grid[0].size())
{
if((grid[i+1][j]!=0)){
sum=sum+lands[grid[i+1][j]];
flag1=grid[i+1][j];
}
}
if((i-1)>=0&&(i-1)<grid.size()&&j>=0&&j<grid[0].size())
{
if((grid[i-1][j]!=0)&&(grid[i-1][j]!=flag1)){
sum=sum+lands[grid[i-1][j]];
flag2=grid[i-1][j];
}
}
if(i>=0&&i<grid.size()&&(j+1)>=0&&(j+1)<grid[0].size())
{
if((grid[i][j+1]!=0)&&(grid[i][j+1]!=flag1)&&(grid[i][j+1]!=flag2)){
sum=sum+lands[grid[i][j+1]];
flag3=grid[i][j+1];
}
}
if(i>=0&&i<grid.size()&&(j-1)>=0&&(j-1)<grid[0].size())
{
if((grid[i][j-1]!=0)&&(grid[i][j-1]!=flag1)&&(grid[i][j-1]!=flag2)&&(grid[i][j-1]!=flag3))
sum=sum+lands[grid[i][j-1]];
}
return sum+1;
}
};