LeetCode 827. 最大人工岛
题目描述


题意分析
最多把一个
0改成1,求修改后最大的四连通岛屿面积。只能上下左右相连,对角线相邻不算连通;也可以不修改任何格子。如果对每个水格都重新搜索整张图,会重复计算原有岛屿。一次翻转只会把这个格子与四周接触到的岛连接起来,因此可以先统计所有原有岛屿,再直接计算每个翻转位置的收益。
解法:染色 + 枚举翻转
核心思路
[!blue]
第一遍扫描把每座岛染成独立编号,并在
area[id]中保存面积。编号从 2 开始,避免与原来的水格0、尚未处理的陆地1混淆。染色使用显式栈,每发现一个1就先写入编号再入栈,因此同一格不会被多个方向重复发现,出栈时只计入面积一次。第二遍枚举每个水格。若把它翻成陆地,新岛面积就是“当前格子的 1,加上四邻中所有不同岛屿的面积”。必须按岛屿编号去重,因为四个相邻陆地格可能本来就属于同一座岛,不能重复加上整座岛的面积。
这个面积恰好覆盖翻转后的整个连通块:与新格相邻的岛都会通过它连接,而不相邻的岛若仍被水隔开,就不可能只靠这一次翻转接入。对所有水格计算一次,再与原有最大岛面积比较,就覆盖了所有可选方案。
先用原有岛屿面积初始化
best,也包含“不翻转”的情况。全陆地时没有水格可枚举,直接保留n*n;全水时没有邻岛,每个候选面积都是 1。题目保证网格非空,这两种情况都不需要额外兜底。
解题步骤
- 从编号 2 开始扫描网格。遇到尚未染色的
1,用栈遍历整座岛,写入统一编号并记录面积,之后增加编号。- 从面积表中取得原有最大岛面积,作为
best的初值。- 再次扫描每个
0,把候选面积sum初始化为 1,并创建当前候选专用的编号集合seen。- 检查四个相邻位置,跳过越界、水格和已经统计的岛编号,把首次遇到的邻岛面积加入
sum。- 用
sum更新best,枚举结束后返回best。染色直接写回网格,因此会修改输入。
代码实现
class Solution {
private int n;
private int[][] grid;
private int id = 2;
public int largestIsland(int[][] grid) {
id = 2;
this.n = grid.length;
this.grid = grid;
Map<Integer, Integer> area = new HashMap<>();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == 1) {
int a = paint(i, j, id);
area.put(id, a);
id++;
}
}
}
int best = 0;
for (int val : area.values()) {
best = Math.max(best, val);
}
int[] dr = {
-1,
1,
0,
0
};
int[] dc = {
0,
0,
-1,
1
};
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] != 0) {
continue;
}
Set<Integer> seen = new HashSet<>();
// 翻转的这个海水格自身贡献一
int sum = 1;
for (int k = 0; k < 4; k++) {
int ni = i + dr[k];
int nj = j + dc[k];
if (ni < 0 || ni >= n || nj < 0 || nj >= n) {
continue;
}
int idx = grid[ni][nj];
// 同一座岛可能从多方向相邻,面积只能累加一次
if (idx > 1 && seen.add(idx)) {
sum += area.get(idx);
}
}
best = Math.max(best, sum);
}
}
return best;
}
private int paint(int r, int c, int idx) {
Deque<int[]> stack = new ArrayDeque<>();
grid[r][c] = idx;
stack.push(new int[] {
r,
c
});
// 每次弹出一个已编号格,面积只增加一次
int res = 0;
int[] dr = {
-1,
1,
0,
0
};
int[] dc = {
0,
0,
-1,
1
};
while (!stack.isEmpty()) {
int[] cell = stack.pop();
res++;
for (int k = 0; k < 4; k++) {
int nr = cell[0] + dr[k];
int nc = cell[1] + dc[k];
if (nr < 0 || nr >= n || nc < 0 || nc >= n || grid[nr][nc] != 1) {
continue;
}
// 入栈前写编号,防止多个相邻格重复加入
grid[nr][nc] = idx;
stack.push(new int[] {
nr,
nc
});
}
}
return res;
}
}
func largestIsland(grid [][]int) int {
n := len(grid)
id := 2
area := make(map[int]int)
paint := func(r int, c int, idx int) int {
grid[r][c] = idx
stack := [][2]int{
{r, c},
}
// 每次弹出一个已编号格,面积只增加一次
res := 0
dirs := [][2]int{
{-1, 0},
{1, 0},
{0, -1},
{0, 1},
}
for len(stack) > 0 {
cell := stack[len(stack)-1]
stack = stack[:len(stack)-1]
res++
for _, d := range dirs {
nr, nc := cell[0]+d[0], cell[1]+d[1]
if nr < 0 || nr >= n || nc < 0 || nc >= n || grid[nr][nc] != 1 {
continue
}
// 入栈前写编号,防止多个相邻格重复加入
grid[nr][nc] = idx
stack = append(stack, [2]int{
nr,
nc,
})
}
}
return res
}
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
if grid[i][j] == 1 {
a := paint(i, j, id)
area[id] = a
id++
}
}
}
best := 0
for _, v := range area {
if v > best {
best = v
}
}
dirs := make([][2]int, 0, 4)
dirs = append(
dirs,
[2]int{
-1,
0,
},
[2]int{
1,
0,
},
[2]int{
0,
-1,
},
[2]int{
0,
1,
},
)
for i := 0; i < n; i++ {
for j := 0; j < n; j++ {
if grid[i][j] != 0 {
continue
}
seen := make(map[int]bool)
// 翻转的这个海水格自身贡献一
sum := 1
for _, d := range dirs {
ni := i + d[0]
nj := j + d[1]
if ni < 0 || ni >= n || nj < 0 || nj >= n {
continue
}
idx := grid[ni][nj]
// 同一座岛可能从多方向相邻,面积只能累加一次
if idx > 1 && !seen[idx] {
seen[idx] = true
sum += area[idx]
}
}
if sum > best {
best = sum
}
}
}
return best
}
复杂度分析
- 时间复杂度:$O(n^2)$。染色时每个陆地格只处理一次;枚举水格时,每个候选只检查四个方向。
- 空间复杂度:$O(n^2)$,用于显式栈和岛屿面积表。每个候选的去重集合最多包含四个岛编号,占常数空间。
关键点总结
[!green]
- 先把原有连通块压缩为“编号和面积”,后续候选只需查询四个邻居。
- 翻转增加一格,并连接所有不同的相邻岛;去重的对象是岛编号。
- 保留原有最大岛面积,才能自然处理没有水格可翻转的情况。
易错点总结
[!yellow]
- 相邻岛不去重:同一座岛可能从多个方向接触候选格,但它的面积只能加一次。
- 染色后仍只识别值为 1 的邻居:已有岛已经变成不小于 2 的编号,应通过编号查询面积。
- 多个翻转候选共用同一个
seen:去重只针对当前候选,每换一个水格都要重新统计邻岛。- 只统计翻转后的候选面积:全陆地时没有候选,会漏掉原有最大岛。
- 出栈时才写编号:同一格可能被多个邻居重复压栈并重复计数,应在入栈前完成标记。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 695. 岛屿的最大面积 | 中等 | 先标记每个已有岛屿及面积,再比较一次翻0能合并的不同邻接分量。 |
| 305. 岛屿数量 II | 困难 | 同样新增陆地后合并相邻分量,本题只选择一次最优新增,原题按给定顺序持续增加。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!