827. 最大人工岛[DFS+岛屿编号]

题解

思路:

翻转0==>1后,需要将周围存在的岛屿的面积做累加。

因此为了方便,我们不在每一次翻转后都去计算一遍岛屿的面积,这样会使得复杂度大幅提升。

可以考虑第一次便利时,我们就将岛屿编号,并记录每个编号岛屿的面积,这样,下次找到岛屿时通过编号可以直接获取面积。

那么问题又来了,如何知道当前格子0附近的岛屿1是哪个编号的岛屿呢?这需要去对原地图进行部分的修改,将原来的岛屿修改为对应的编号,我们做如下修改:

  1. 每个连通的岛屿给不同的编号
  2. 岛屿周围的一圈赋值为-1

因为只翻转一格,所以如果想获得岛屿的最大值,一定是在岛屿周围一圈的格子翻转

  1. 其余格子值不变

修改方式如图所示:

Solution827image01.jpeg

问题就转变为几个小问题了:

  1. 计算每个岛屿的面积并编号。可以利用DFS计算岛屿面积,并通过一个值记录当前岛屿的最大编号,并利用HasMap记录编号岛屿对应的面积。
  2. 转换完成后,遍历寻找-1的格子,找到周围所有不同编号的岛屿,计算面积之和并加1,取最大值。
  3. 考虑极端情况,整个区域都是1,返回结果为n*n;整个区域都为0,返回结果为1

实现代码如下:

public class Solution {
    private final int[] directionX = {-1,0,1,0};
    private final int[] directionY = {0,1,0,-1};

    private int[][] idGrid;
    private Map<Integer, Integer> id2Area = new HashMap<>();
    private int n;
    private int curArea;

    public int largestIsland(int[][] grid) {
        //1. 通过DFS给每个岛屿编号,同时计算面积
        int count = 1;
        int ans = 0;
        int n = grid.length;
        this.n = n;
        idGrid = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 1 && idGrid[i][j]==0){
                    // 清空上次岛屿的面积
                    curArea = 0;
                    //计算当前岛屿面积
                    dfs(grid, i, j, count);
                    id2Area.put(count, curArea);

                    count++;
                    ans = Math.max(ans, curArea);  //考虑地图填满的情况,没有0->1的操作空间
                }
            }
        }

        //在idGrid上做文章,-1的部分为岛屿的边界
        //0的部分没有用
        //大于0的部分为岛屿

        int newX, newY;
        HashSet<Integer> idSet = new HashSet<>();

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (idGrid[i][j] < 0){
                    //说明四个方向上至少有一个岛屿
                    //可以做以下的统一处理
                    //获取4个方向上岛屿的面积,如果不存在岛屿,面积值设置为0,
                    //把这四个方向上岛屿的面积全部加起来再+1即可

                    //拿到四个岛屿的编号,可能会存在重复的,使用集合
                    idSet.clear();
                    for (int k = 0; k < 4; k++) {
                        newX = i+directionX[k];
                        newY = j+directionY[k];
                        if (newX>=0 && newX<n && newY>=0 && newY<n){
                            idSet.add(idGrid[newX][newY]);
                        }
                    }

                    //把这几个编号的面积全部加上
                    int tempArea = 0;
                    for (Integer id : idSet) {
                        tempArea += (id2Area.getOrDefault(id, 0));
                    }
                    ans = Math.max(ans, tempArea+1);
                    
                }
            }
        }
        
        //考虑没有岛屿的情况
        return ans==0 ? 1:ans;
    }

    private void dfs(int[][] grid, int x, int y, int id){
        idGrid[x][y] = id;
        curArea++;

        int newX, newY;
        for (int i=0; i<4; i++){
            newX = x+directionX[i];
            newY = y+directionY[i];
            if (newX>=0 && newX < n && newY>=0 && newY<n){
                if(grid[newX][newY] == 1 && idGrid[newX][newY]==0){
                    dfs(grid, newX, newY, id);
                } else if(grid[newX][newY] == 0) {
                    //岛屿周围做一个标记
                    idGrid[newX][newY] = -1;
                }
            }
        }
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值