[LeetCode][Hard] Making A Large Island 最大人工岛 | Java

3,103 阅读2分钟

问题

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.length
  • n == grid[i].length
  • 1 <= n <= 500
  • grid[i][j] is either 0 or 1.

解题思路

本题可以分解为两个子问题:

  1. 找出每个岛屿,并进行标记,同时计算岛屿的面积
  2. 试图填海造陆,连接相近的岛屿,得到最大的人工岛(尽可能使用子问题1中求出的岛屿面积)

对于子问题1,我们注意到岛屿的定义是4个方向上接壤的所有的陆地表示一个岛,网格grid中的1表示陆地。我们可以这样做:

  1. 遍历grid,直到碰到陆地,即grid[row][col] == 1
  2. 开始寻找与其接壤的所有陆地,同时累计走过的陆地数,用于计算岛屿面积,直到走过每一寸接壤的陆地
  3. 重复上述步骤,直到遍历完grid,需要注意步骤1中需要跳过已经在步骤2中标记过的陆地

为了在子问题2中使用子问题1中的计算结果,我们需要区别不同的岛屿,那么就需要对岛屿进行编号,并且要将岛屿编号对应的面积保存下来,比如用List<Integer> islandAreaList保存岛屿面积。与此同时,还需要将grid中的哪块陆地属于哪个岛屿这个信息记录下来。我们注意到grid中用于表示陆地的1,用于计算岛屿面积之后就不再需要了,我们可以废物再利用,将岛屿编号保存岛grid中,因为01已经被使用了,所以岛屿的编号应该从2开始。

另外,为了从一块陆地寻找到与其接壤的所有陆地,需要用到深度优先搜索(DFS),这是一种用于遍历或搜索树或图的算法。这个算法会尽可能深的搜索树的分支,直到已发现从源节点可达的所有节点为止。在DFS过程中,为了避免重复访问,可以在访问到某节点之后,将grid[row][col]设置为岛屿编号,由于岛屿编号>=2,不满足grid[row][col] == 1的条件,所以这个节点不会再被访问。到此子问题1就差不多解决完了。

对于子问题2,我们可以这么做:

  1. 遍历grid,直到碰到海洋,即grid[row][col] == 0
  2. grid找到与其相邻的所有的岛屿编号,注意岛屿编号不能重复
  3. 利用岛屿编号从islandAreaList中得到对应的岛屿面积
  4. 累加得到人工岛面积,并记录下最大的人工岛面积

参考答案

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;
    }
}

image.png

拓展训练

来试试这个同样使用的DFS的问题吧!

[Leetcode][Medium] Count Good Nodes in Binary Tree 统计二叉树中好节点的数目 | Java

或者到作者的LeetCode专栏中看看,有没有其他感兴趣的问题吧!

juejin.cn/column/6997…

资料链接