(图论) 827. 最大人工岛 ——【Leetcode每日一题】

❓ 827. 最大人工岛

难度:困难

给你一个大小为 n x n 二进制矩阵 grid 。最多 只能将一格 0 变成 1

返回执行此操作后,grid 中最大的岛屿面积是多少?

岛屿 由一组上、下、左、右四个方向相连的 1 形成。

示例 1:

输入: grid = [[1, 0], [0, 1]]

输出: 3

解释: 将一格0变成1,最终连通两个小岛得到面积为 3 的岛屿。

示例 2:

输入: grid = [[1, 1], [1, 0]]

输出: 4

解释: 将一格0变成1,岛屿的面积扩大为 4。

示例 3:

输入: grid = [[1, 1], [1, 1]]

输出: 4

解释: 没有0可以让我们变成1,面积依然为 4。

提示

  • n == grid.length
  • n == grid[i].length
  • 1 <= n <= 500
  • grid[i][j]01

💡思路:

本题的一个暴力想法 ,应该是遍历地图尝试 将每一个 0 改成1,然后去搜索地图中的最大的岛屿面积。

本题使用 深搜 还是 广搜 都是可以的,其目的就是遍历岛屿做一个标记,相当于染色,那么使用哪个遍历方式都行,我用的是深搜。

根据暴力的想法每次深搜遍历计算最大岛屿面积,我们都做了很多重复的工作。只要用一次深搜把每个岛屿的面积记录下来就好。

  1. 一次遍历地图,得出各个岛屿的面积,并做编号记录。可以使用map 记录,key 为岛屿编号,value 为岛屿面积,我是直接在原 grid 上直接标记;
  2. 在遍历地图,遍历 0 的方格(因为要将 0 变成 1),并统计该1(由 0 变成的1)周边岛屿面积,将其相邻面积相加在一起 ,遍历所有 0 之后,就可以得出 选一个0变成1 之后的最大面积。

🍁代码:(C++、Java)

C++

cpp 复制代码
class Solution {
private:
    int cnt;
    int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
    void dfs(vector<vector<int>>& grid, int x, int y, int mark){
        if(grid[x][y] == 0 || grid[x][y] != 1) return; // 遇到海水或访问过的节点,则终止
        grid[x][y] = mark; //给陆地标记新的标签
        cnt++;
        for(int i = 0; i < 4; i++){
            int nextx = x + dir[i][0];
            int nexty = y + dir[i][1];
            if(nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
            dfs(grid, nextx, nexty, mark);
        }
    }
public:
    int largestIsland(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        unordered_map<int, int> gridNum;
        int mark = 2;
        bool isAllGrid = true; // 标记是否整个地图都是陆地
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(grid[i][j] == 0) isAllGrid = false;
                if(grid[i][j] == 1){
                    cnt = 0;
                    dfs(grid, i, j, mark);
                    gridNum[mark] = cnt;
                    mark++;
                }
            }
        }
        if (isAllGrid) return n * m; // 如果都是陆地,返回全面积

        // 以下逻辑是根据添加陆地的位置,计算周边岛屿面积之和
        int ans = 0;
        unordered_set<int> visitedGrid;  // 标记访问过的岛屿
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                int num = 1;
                visitedGrid.clear(); // 每次使用时,清空
                if(grid[i][j] == 0){
                    for(int k = 0; k < 4; k++){
                        int neari = i + dir[k][0];
                        int nearj = j + dir[k][1];
                        if (neari < 0 || neari >= grid.size() || nearj < 0 || nearj >= grid[0].size()) continue;
                        if (visitedGrid.count(grid[neari][nearj])) continue;
                         // 把相邻四面的岛屿数量加起来
                        num += gridNum[grid[neari][nearj]];
                        visitedGrid.insert(grid[neari][nearj]); // 标记该岛屿已经添加
                    }
                }
                ans = max(ans, num);
            }
        }
        return ans;
    }
};

Java

java 复制代码
class Solution {
    private static final int[][] dir = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; // 四个方向
    private int cnt;
    private void dfs(int[][] grid, int x, int y, int mark){
        if(grid[x][y] != 1) return;
        grid[x][y] = mark;
        cnt++;
        for(int i = 0; i < 4; i++){
            int nextx = x + dir[i][0];
            int nexty = y + dir[i][1];
            if(nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length) continue;
            dfs(grid, nextx, nexty, mark);
        }
    }
    public int largestIsland(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        Map<Integer, Integer> gridNum = new HashMap<>();
        int mark = 2;
        boolean isAllGrid = true;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(grid[i][j] == 0) isAllGrid = false;
                if(grid[i][j] == 1){
                    cnt = 0;
                    dfs(grid, i, j, mark);
                    gridNum.put(mark++, cnt);
                }
            }
        }
        if(isAllGrid) return n * m;

        int ans = 0;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                Set<Integer> hashSet = new HashSet<>();     // 防止同一个区域被重复计算
                int num = 1;
                if(grid[i][j] == 0){
                    for(int k = 0; k < 4; k++){
                        int neari = i + dir[k][0];
                        int nearj = j + dir[k][1];
                        if(neari < 0 || neari >= grid.length || nearj < 0 || nearj >= grid[0].length) continue;
                        int curMark = grid[neari][nearj];     // 获取对应位置的标记
                        if (hashSet.contains(curMark) || !gridNum.containsKey(curMark)) continue;
                        num += gridNum.get(curMark);
                        hashSet.add(curMark);

                    }
                    ans = Math.max(ans, num);
                }
            }
        }
        return ans;
    }
}
🚀 运行结果:
🕔 复杂度分析:
  • 时间复杂度 : O ( m n ) O(mn) O(mn),其中 mngrid的高和宽。方格地图中,每个节点我们就遍历一次,并不会重复遍历。
  • 空间复杂度 : O ( m n ) O(mn) O(mn)。

题目来源:力扣。

放弃一件事很容易,每天能坚持一件事一定很酷,一起每日一题吧!
关注我LeetCode主页 / CSDN---力扣专栏,每日更新!

注: 如有不足,欢迎指正!
相关推荐
ChoSeitaku4 分钟前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
DdddJMs__1359 分钟前
C语言 | Leetcode C语言题解之第557题反转字符串中的单词III
c语言·leetcode·题解
Fuxiao___12 分钟前
不使用递归的决策树生成算法
算法
我爱工作&工作love我17 分钟前
1435:【例题3】曲线 一本通 代替三分
c++·算法
白-胖-子1 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
workflower1 小时前
数据结构练习题和答案
数据结构·算法·链表·线性回归
好睡凯1 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法
Sunyanhui11 小时前
力扣 二叉树的直径-543
算法·leetcode·职场和发展
一个不喜欢and不会代码的码农1 小时前
力扣105:从先序和中序序列构造二叉树
数据结构·算法·leetcode
前端郭德纲1 小时前
浏览器是加载ES6模块的?
javascript·算法