力扣第827题 最大人工岛 C++ 深度优先搜索 附Java代码

题目

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] 为 0 或 1

思路和解题方法

首先我们定义了一个私有变量 count 和一个二维数组 dir,用于表示四个方向的移动。接着有一个私有函数 dfs,用于深度优先搜索并标记相邻的陆地块。

在公共部分,我们有一个公共函数 largestIsland,它首先初始化一些变量,然后通过 DFS 标记每个岛屿,并记录每个岛屿的面积。接着,对于每个海洋块,计算连接的岛屿面积之和,并找出最大值作为结果。

整体逻辑可以分为以下几个步骤:

  1. 初始化:定义变量和数据结构,包括记录岛屿面积的变量、四个方向的数组、以及标记是否整个地图都是陆地的变量。
  2. 深度优先搜索和标记:遍历地图,对于每个未访问过的陆地块,使用深度优先搜索标记与其相邻的陆地块,并记录每个岛屿的面积。
  3. 计算连接的岛屿面积之和:遍历海洋块,计算连接的岛屿面积之和,并找出最大值作为结果。

在第三步中,对于每个海洋块,我们会判断其周围相邻的岛屿,并计算连接的岛屿面积之和。最后找出最大值作为结果返回。

复杂度

时间复杂度分析:

  1. 深度优先搜索标记岛屿:在遍历整个二维网格时,每个格子最多会被访问两次(一次在初始化时,一次在计算相邻岛屿面积时)。因此,深度优先搜索的时间复杂度为 O(n*m),其中 n 为网格的行数,m 为网格的列数。
  2. 计算连接的岛屿面积之和:再次遍历整个二维网格,对于每个海洋块,最多需要考虑其周围4个相邻格子,因此计算连接的岛屿面积之和的时间复杂度也为 O(n*m)。

因此,整体的时间复杂度为 O(n*m),其中 n 为网格的行数,m 为网格的列数。

空间复杂度分析:

  1. 我们使用一个与原始网格大小相同的 visited 数组来标记访问过的点,因此其空间复杂度为 O(n*m)。
  2. 另外,我们使用了一个 unordered_map 来存储每个岛屿的面积,以及一个 unordered_set 来标记访问过的岛屿,在最坏情况下,它们的空间复杂度也为 O(n*m)。

因此,整体的空间复杂度为 O(n*m),其中 n 为网格的行数,m 为网格的列数。

c++ 代码

// 计算给定二维网格中最大的岛屿面积
class Solution {
private:
    int count; // 用于记录岛屿面积
    int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向

    // 深度优先搜索标记相邻的陆地块
    void dfs(vector>& grid, vector>& visited, int x, int y, int mark) {
        if (visited[x][y] || grid[x][y] == 0) return; // 终止条件:访问过的节点 或者 遇到海水
        visited[x][y] = true; // 标记访问过
        grid[x][y] = mark; // 给陆地标记新标签
        count++; // 岛屿面积加一
        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, visited, nextx, nexty, mark);
        }
    }

public:
    // 计算最大岛屿面积
    int largestIsland(vector>& grid) {
        int n = grid.size(), m = grid[0].size();
        vector> visited = vector>(n, vector(m, false)); // 标记访问过的点
        unordered_map gridNum; // 存储每个岛屿的面积
        int mark = 2; // 记录每个岛屿的编号
        bool isAllGrid = true; // 标记是否整个地图都是陆地
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 0) isAllGrid = false;
                if (!visited[i][j] && grid[i][j] == 1) {
                    count = 0;
                    dfs(grid, visited, i, j, mark); // 将与其链接的陆地都标记上 true
                    gridNum[mark] = count; // 记录每一个岛屿的面积
                    mark++; // 记录下一个岛屿编号
                }
            }
        }
        if (isAllGrid) return n * m; // 如果都是陆地,返回全面积

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

附Java代码

class Solution {
    private static final int[][] position = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};  // 四个方向

    /**
     * @param grid 矩阵数组
     * @param row 当前遍历的节点的行号
     * @param col 当前遍历的节点的列号
     * @param mark 当前区域的标记
     * @return 返回当前区域内 1 的数量
     */
    public int dfs(int[][] grid, int row, int col, int mark) {
        int ans = 0;
        grid[row][col] = mark;  // 将当前位置标记为指定标记
        for (int[] current: position) {
            int curRow = row + current[0], curCol = col + current[1];
            if (curRow < 0 || curRow >= grid.length || curCol < 0 || curCol >= grid.length) continue;  // 越界检查
            if (grid[curRow][curCol] == 1)
                ans += 1 + dfs(grid, curRow, curCol, mark);  // 统计当前区域内 1 的数量
        }
        return ans;
    }

    public int largestIsland(int[][] grid) {
        int ans = Integer.MIN_VALUE, size = grid.length, mark = 2;  // 初始化变量
        Map getSize = new HashMap<>();  // 用于存储每个岛屿区域的大小
        for (int row = 0; row < size; row++) {
            for (int col = 0; col < size; col++) {
                if (grid[row][col] == 1) {
                    int areaSize = 1 + dfs(grid, row, col, mark);  // 计算当前岛屿区域的大小
                    getSize.put(mark++, areaSize);  // 将岛屿区域的大小存入map中
                }
            }
        }
        for (int row = 0; row < size; row++) {
            for (int col = 0; col < size; col++) {
                if (grid[row][col] != 0) continue;  // 如果当前位置不是 0,则跳过
                Set hashSet = new HashSet<>();  // 用于防止重复计算同一区域
                int curSize = 1;  // 初始化当前区域的大小为 1
                for (int[] current: position) {
                    int curRow = row + current[0], curCol = col + current[1];
                    if (curRow < 0 || curRow >= grid.length || curCol < 0 || curCol >= grid.length) continue;
                    int curMark = grid[curRow][curCol];  // 获取对应位置的标记
                    if (hashSet.contains(curMark) || !getSize.containsKey(curMark)) continue;  // 检查是否已经处理过该区域
                    hashSet.add(curMark);
                    curSize += getSize.get(curMark);  // 更新当前区域的大小
                }
                ans = Math.max(ans, curSize);  // 更新最大岛屿大小
            }
        }
        // 当 ans == Integer.MIN_VALUE 说明矩阵数组中不存在 0,全都是有效区域,返回数组大小即可
        return ans == Integer.MIN_VALUE ? size * size : ans;
    }
}

觉得有用的话可以点点赞,支持一下。

如果愿意的话关注一下。会对你有更多的帮助。

每天都会不定时更新哦  >人<  。

你可能感兴趣的:(leetcode,深度优先搜索,广度优先搜索,数据结构,leetcode,c++,深度优先,算法,java)