原文链接: https://leetcode-cn.com/problems/making-a-large-island
英文原文
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 1
s.
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 either0
or1
.
中文题目
给你一个大小为 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
通过代码
官方题解
方法 1:深度优先搜索【超时】
想法
对于每个 0
,我们将它暂时变成 1
,然后统计这个连通块的大小。
算法
对于每个 0
,将它变成 1
,然后做一次深度优先搜索计算出连通块的大小。答案就是找到的最大连通块。
当然,如果没有 0
,那么答案就是整个网格的大小。
[]class Solution { int[] dr = new int[]{-1, 0, 1, 0}; int[] dc = new int[]{0, -1, 0, 1}; public int largestIsland(int[][] grid) { int N = grid.length; int ans = 0; boolean hasZero = false; for (int r = 0; r < N; ++r) for (int c = 0; c < N; ++c) if (grid[r][c] == 0) { hasZero = true; grid[r][c] = 1; ans = Math.max(ans, check(grid, r, c)); grid[r][c] = 0; } return hasZero ? ans : N*N; } public int check(int[][] grid, int r0, int c0) { int N = grid.length; Stack<Integer> stack = new Stack(); Set<Integer> seen = new HashSet(); stack.push(r0 * N + c0); seen.add(r0 * N + c0); while (!stack.isEmpty()) { int code = stack.pop(); int r = code / N, c = code % N; for (int k = 0; k < 4; ++k) { int nr = r + dr[k], nc = c + dc[k]; if (!seen.contains(nr * N + nc) && 0 <= nr && nr < N && 0 <= nc && nc < N && grid[nr][nc] == 1) { stack.push(nr * N + nc); seen.add(nr * N + nc); } } } return seen.size(); } }
[]class Solution(object): def largestIsland(self, grid): N = len(grid) def check(r, c): seen = {(r, c)} stack = [(r, c)] while stack: r, c = stack.pop() for nr, nc in ((r-1, c), (r, c-1), (r+1, c), (r, c+1)): if (nr, nc) not in seen and 0 <= nr < N and 0 <= nc < N and grid[nr][nc]: stack.append((nr, nc)) seen.add((nr, nc)) return len(seen) ans = 0 has_zero = False for r, row in enumerate(grid): for c, val in enumerate(row): if val == 0: has_zero = True grid[r][c] = 1 ans = max(ans, check(r, c)) grid[r][c] = 0 return ans if has_zero else N*N
复杂度分析
- 时间复杂度:$O(N^4)$,其中 $N$ 是
grid
的长和宽。 - 空间复杂度:$O(N^2)$,深度优先搜索需要的
stack
和seen
的空间。
方法 2:连通块大小【通过】
想法
再上一个解法中,我们检查了每个 0
。然而,我们也计算了每个组的大小,所以其实并不需要利用深度优先搜索重复计算所有的连通块。
然而,这个方法会失败如果 0
和相同的连通块相交。例如,考虑 grid = [[0,1],[1,1]]
答案是 4
而不是 1 + 3 + 3
,因为 0
右边的邻居和底下的邻居属于同一连通块。
我们可以通过记录连通块编号来解决这个问题,不同的连通块编号不同。这样,我们就可以累加不同编号的连通块面积和。
算法
对于每个连通块,将所有格点赋值为 index
并记录他们的大小 area[index] = dfs(...)
。
然后对于每个 0
,查看周围的邻居编号在 seen
并将这个区域的大小加入结果,改变 seen
的值。这就是当前节点的面积大小,在其中找到最大的。
为了解决没有 0
的情况,我们只需要记录之前计算的最大面积并输出即可。
[]class Solution { int[] dr = new int[]{-1, 0, 1, 0}; int[] dc = new int[]{0, -1, 0, 1}; int[][] grid; int N; public int largestIsland(int[][] grid) { this.grid = grid; N = grid.length; int index = 2; int[] area = new int[N*N + 2]; for (int r = 0; r < N; ++r) for (int c = 0; c < N; ++c) if (grid[r][c] == 1) area[index] = dfs(r, c, index++); int ans = 0; for (int x: area) ans = Math.max(ans, x); for (int r = 0; r < N; ++r) for (int c = 0; c < N; ++c) if (grid[r][c] == 0) { Set<Integer> seen = new HashSet(); for (Integer move: neighbors(r, c)) if (grid[move / N][move % N] > 1) seen.add(grid[move / N][move % N]); int bns = 1; for (int i: seen) bns += area[i]; ans = Math.max(ans, bns); } return ans; } public int dfs(int r, int c, int index) { int ans = 1; grid[r][c] = index; for (Integer move: neighbors(r, c)) { if (grid[move / N][move % N] == 1) { grid[move / N][move % N] = index; ans += dfs(move / N, move % N, index); } } return ans; } public List<Integer> neighbors(int r, int c) { List<Integer> ans = new ArrayList(); for (int k = 0; k < 4; ++k) { int nr = r + dr[k]; int nc = c + dc[k]; if (0 <= nr && nr < N && 0 <= nc && nc < N) ans.add(nr * N + nc); } return ans; } }
[]class Solution(object): def largestIsland(self, grid): N = len(grid) def neighbors(r, c): for nr, nc in ((r-1, c), (r+1, c), (r, c-1), (r, c+1)): if 0 <= nr < N and 0 <= nc < N: yield nr, nc def dfs(r, c, index): ans = 1 grid[r][c] = index for nr, nc in neighbors(r, c): if grid[nr][nc] == 1: ans += dfs(nr, nc, index) return ans area = {} index = 2 for r in xrange(N): for c in xrange(N): if grid[r][c] == 1: area[index] = dfs(r, c, index) index += 1 ans = max(area.values() or [0]) for r in xrange(N): for c in xrange(N): if grid[r][c] == 0: seen = {grid[nr][nc] for nr, nc in neighbors(r, c) if grid[nr][nc] > 1} ans = max(ans, 1 + sum(area[i] for i in seen)) return ans
复杂度分析
- 时间复杂度:$O(N^2)$,其中 $N$ 是
grid
的长度和宽度。 - 空间复杂度:$O(N^2)$,深度优先搜索需要的数组
area
的额外空间。
统计信息
通过次数 | 提交次数 | AC比率 |
---|---|---|
10106 | 25850 | 39.1% |
提交历史
提交时间 | 提交结果 | 执行时间 | 内存消耗 | 语言 |
---|