原文链接: 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% |
提交历史
提交时间 | 提交结果 | 执行时间 | 内存消耗 | 语言 |
---|