问题
Making A Large Island Hard
You are given an n x n binary matrix grid. You are allowed to change at most one 0 to be 1.
Return the size of the largest island in grid after applying this operation.
An island is a 4-directionally connected group of 1s.
Example 1:
Input: grid = [[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: grid = [[1,1],[1,0]]
Output: 4
Explanation: Change the 0 to 1 and make the island bigger, only one island with area = 4.
Example 3:
Input: grid = [[1,1],[1,1]]
Output: 4
Explanation: Can't change any 0 to 1, only one island with area = 4.
Constraints:
n == grid.lengthn == grid[i].length1 <= n <= 500grid[i][j]is either0or1.
解题思路
本题可以分解为两个子问题:
- 找出每个岛屿,并进行标记,同时计算岛屿的面积
- 试图填海造陆,连接相近的岛屿,得到最大的人工岛(尽可能使用子问题
1中求出的岛屿面积)
对于子问题1,我们注意到岛屿的定义是4个方向上接壤的所有的陆地表示一个岛,网格grid中的1表示陆地。我们可以这样做:
- 遍历
grid,直到碰到陆地,即grid[row][col] == 1 - 开始寻找与其接壤的所有陆地,同时累计走过的陆地数,用于计算岛屿面积,直到走过每一寸接壤的陆地
- 重复上述步骤,直到遍历完
grid,需要注意步骤1中需要跳过已经在步骤2中标记过的陆地
为了在子问题2中使用子问题1中的计算结果,我们需要区别不同的岛屿,那么就需要对岛屿进行编号,并且要将岛屿编号对应的面积保存下来,比如用List<Integer> islandAreaList保存岛屿面积。与此同时,还需要将grid中的哪块陆地属于哪个岛屿这个信息记录下来。我们注意到grid中用于表示陆地的1,用于计算岛屿面积之后就不再需要了,我们可以废物再利用,将岛屿编号保存岛grid中,因为0和1已经被使用了,所以岛屿的编号应该从2开始。
另外,为了从一块陆地寻找到与其接壤的所有陆地,需要用到深度优先搜索(DFS),这是一种用于遍历或搜索树或图的算法。这个算法会尽可能深的搜索树的分支,直到已发现从源节点可达的所有节点为止。在DFS过程中,为了避免重复访问,可以在访问到某节点之后,将grid[row][col]设置为岛屿编号,由于岛屿编号>=2,不满足grid[row][col] == 1的条件,所以这个节点不会再被访问。到此子问题1就差不多解决完了。
对于子问题2,我们可以这么做:
- 遍历
grid,直到碰到海洋,即grid[row][col] == 0 - 从
grid找到与其相邻的所有的岛屿编号,注意岛屿编号不能重复 - 利用岛屿编号从
islandAreaList中得到对应的岛屿面积 - 累加得到人工岛面积,并记录下最大的人工岛面积
参考答案
public class Solution {
// 4 directions connected to one cell
final int[] rowBoundary = {-1, 1, 0, 0};
final int[] colBoundary = { 0, 0, -1, 1};
public int largestIsland(int[][] grid) {
int n = grid.length;
List<Integer> islandAreaList = new ArrayList<>();
// make initial list size to 2
islandAreaList.add(-1);
islandAreaList.add(-1);
int maxArea = 0;
for (int row = 0; row < n; row++) {
for (int col = 0; col < n; col++) {
if (grid[row][col] == 1) {
int area = DFS(row, col, grid, islandAreaList.size());
islandAreaList.add(area);
maxArea = area;
}
}
}
// a set to save island No. during connecting islands, to avoid duplicate sum
HashSet<Integer> islandSet = new HashSet<>();
for (int row = 0; row < n; row++) {
for (int col = 0; col < n; col++) {
if (grid[row][col] == 0) {
int sum = 0;
islandSet.clear();
// move to connected cells
for (int i = 0; i < rowBoundary.length; i++) {
int r = row + rowBoundary[i];
int c = col + colBoundary[i];
// island area can be only summed by once
if (isValid(r, c, grid) && grid[r][c] > 0 && !islandSet.contains(grid[r][c])) {
sum += islandAreaList.get(grid[r][c]);
islandSet.add(grid[r][c]);
}
}
// the final area should +1 since we change current 0 to 1
if (sum + 1 > maxArea) {
maxArea = sum + 1;
}
}
}
}
return maxArea == 0 ? 1 : maxArea;
}
private int DFS(int row, int col, int[][] grid, int islandNo) {
// set the islandNo to grid
grid[row][col] = islandNo;
// the area of the island
int area = 1;
// move to connected cells
for (int i = 0; i < rowBoundary.length; i++) {
int r = row + rowBoundary[i];
int c = col + colBoundary[i];
if (isValid(r, c, grid) && grid[r][c] == 1) {
area += DFS(r, c, grid, islandNo);
}
}
return area;
}
private boolean isValid(int row, int col, int[][] grid) {
return row >= 0 && row < grid.length && col >=0 && col < grid[0].length;
}
}
拓展训练
来试试这个同样使用的DFS的问题吧!
[Leetcode][Medium] Count Good Nodes in Binary Tree 统计二叉树中好节点的数目 | Java
或者到作者的LeetCode专栏中看看,有没有其他感兴趣的问题吧!