827. 最大人工岛
题目描述
输入 n x n 的二进制矩阵 grid,1 是陆地,0 是海水。沿上下左右四个方向连在一起的陆地构成一个岛屿,岛屿面积是其中 1 的个数。
你最多可以把一个 0 改成 1,也可以不改。
返回操作后矩阵中面积最大的岛屿的面积。
输入:grid = [[1,0],[0,1]] 输出:3
解释:把任意一个 0 改成 1,就能把两块陆地连成面积为 3 的岛。
输入:grid = [[1,1],[1,0]] 输出:4
解释:把唯一的 0 改成 1,得到面积为 4 的岛。
输入:grid = [[1,1],[1,1]] 输出:4
解释:没有 0 可改,整个矩阵就是一个面积为 4 的岛。
数据范围
n == grid.lengthn == grid[i].length1 <= n <= 500grid[i][j]为0或1。
解题思路:岛屿标号 + 枚举填海
分两遍。第一遍给每个岛编号(从 2 开始,以免与 0、1 冲突),用 DFS 把整个岛的格子都改成编号,同时记录 area[编号] = 面积。第二遍枚举每个 0,看它上下左右分别属于哪些岛,编号去重后把这些岛的面积加起来再加 1(它自己)。所有候选与原本最大的岛取最大值。
为什么正确
第一遍结束后,grid 中每个陆地格子的值都是它所属岛的编号,area 记录每个编号对应的岛面积。
复杂度分析
时间 O(n²):每个格子在第一遍被访问常数次,第二遍每个 0 只看 4 个邻居。空间 O(n²)。
易错点
同一个岛可能从 0 的两个方向相邻,必须按编号去重,不能重复加面积。另外全是 1 的矩阵没有 0 可改,答案就是 n²,best 要用原有最大岛初始化。
参考代码
Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。
class Solution:
def largestIsland(self, grid: List[List[int]]) -> int:
n = len(grid)
area = {}
label = 2
for r in range(n):
for c in range(n):
if grid[r][c] == 1:
grid[r][c] = label
stack = [(r, c)]
size = 0
while stack:
x, y = stack.pop()
size += 1
for nx, ny in ((x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)):
if 0 <= nx < n and 0 <= ny < n and grid[nx][ny] == 1:
grid[nx][ny] = label
stack.append((nx, ny))
area[label] = size
label += 1
best = max(area.values(), default=0)
for r in range(n):
for c in range(n):
if grid[r][c] == 0:
ids = set()
for nx, ny in ((r + 1, c), (r - 1, c), (r, c + 1), (r, c - 1)):
if 0 <= nx < n and 0 <= ny < n and grid[nx][ny] > 1:
ids.add(grid[nx][ny])
best = max(best, 1 + sum(area[i] for i in ids))
return bestclass Solution {
public int largestIsland(int[][] grid) {
int n = grid.length;
Map<Integer, Integer> area = new HashMap<>();
int label = 2;
int[] dx = {1, -1, 0, 0}, dy = {0, 0, 1, -1};
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
if (grid[r][c] == 1) {
grid[r][c] = label;
Deque<int[]> stk = new ArrayDeque<>();
stk.push(new int[]{r, c});
int size = 0;
while (!stk.isEmpty()) {
int[] p = stk.pop();
size++;
for (int d = 0; d < 4; d++) {
int nx = p[0] + dx[d], ny = p[1] + dy[d];
if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] == 1) {
grid[nx][ny] = label;
stk.push(new int[]{nx, ny});
}
}
}
area.put(label, size);
label++;
}
}
}
int best = 0;
for (int v : area.values()) best = Math.max(best, v);
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
if (grid[r][c] == 0) {
Set<Integer> ids = new HashSet<>();
for (int d = 0; d < 4; d++) {
int nx = r + dx[d], ny = c + dy[d];
if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] > 1) {
ids.add(grid[nx][ny]);
}
}
int s = 1;
for (int i : ids) s += area.get(i);
best = Math.max(best, s);
}
}
}
return best;
}
}class Solution {
public:
int largestIsland(vector<vector<int>>& grid) {
int n = grid.size();
unordered_map<int, int> area;
int label = 2;
int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
if (grid[r][c] == 1) {
grid[r][c] = label;
vector<pair<int, int>> stk = {{r, c}};
int size = 0;
while (!stk.empty()) {
auto [x, y] = stk.back();
stk.pop_back();
size++;
for (int d = 0; d < 4; d++) {
int nx = x + dx[d], ny = y + dy[d];
if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] == 1) {
grid[nx][ny] = label;
stk.push_back({nx, ny});
}
}
}
area[label] = size;
label++;
}
}
}
int best = 0;
for (auto& [k, v] : area) best = max(best, v);
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
if (grid[r][c] == 0) {
set<int> ids;
for (int d = 0; d < 4; d++) {
int nx = r + dx[d], ny = c + dy[d];
if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] > 1) {
ids.insert(grid[nx][ny]);
}
}
int s = 1;
for (int i : ids) s += area[i];
best = max(best, s);
}
}
}
return best;
}
};func largestIsland(grid [][]int) int {
n := len(grid)
area := map[int]int{}
label := 2
dx := []int{1, -1, 0, 0}
dy := []int{0, 0, 1, -1}
for r := 0; r < n; r++ {
for c := 0; c < n; c++ {
if grid[r][c] == 1 {
grid[r][c] = label
stk := [][2]int{{r, c}}
size := 0
for len(stk) > 0 {
p := stk[len(stk)-1]
stk = stk[:len(stk)-1]
size++
for d := 0; d < 4; d++ {
nx, ny := p[0]+dx[d], p[1]+dy[d]
if nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] == 1 {
grid[nx][ny] = label
stk = append(stk, [2]int{nx, ny})
}
}
}
area[label] = size
label++
}
}
}
best := 0
for _, v := range area {
if v > best {
best = v
}
}
for r := 0; r < n; r++ {
for c := 0; c < n; c++ {
if grid[r][c] == 0 {
ids := map[int]bool{}
for d := 0; d < 4; d++ {
nx, ny := r+dx[d], c+dy[d]
if nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] > 1 {
ids[grid[nx][ny]] = true
}
}
s := 1
for i := range ids {
s += area[i]
}
if s > best {
best = s
}
}
}
}
return best
}var largestIsland = function(grid) {
const n = grid.length;
const area = new Map();
let label = 2;
const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];
for (let r = 0; r < n; r++) {
for (let c = 0; c < n; c++) {
if (grid[r][c] === 1) {
grid[r][c] = label;
const stack = [[r, c]];
let size = 0;
while (stack.length > 0) {
const [x, y] = stack.pop();
size++;
for (const [ax, ay] of dirs) {
const nx = x + ax, ny = y + ay;
if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] === 1) {
grid[nx][ny] = label;
stack.push([nx, ny]);
}
}
}
area.set(label, size);
label++;
}
}
}
let best = Math.max(0, ...area.values());
for (let r = 0; r < n; r++) {
for (let c = 0; c < n; c++) {
if (grid[r][c] === 0) {
const ids = new Set();
for (const [ax, ay] of dirs) {
const nx = r + ax, ny = c + ay;
if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] > 1) {
ids.add(grid[nx][ny]);
}
}
let s = 1;
for (const i of ids) s += area.get(i);
best = Math.max(best, s);
}
}
}
return best;
};function largestIsland(grid: number[][]): number {
const n = grid.length;
const area = new Map<number, number>();
let label = 2;
const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];
for (let r = 0; r < n; r++) {
for (let c = 0; c < n; c++) {
if (grid[r][c] === 1) {
grid[r][c] = label;
const stack: number[][] = [[r, c]];
let size = 0;
while (stack.length > 0) {
const [x, y] = stack.pop()!;
size++;
for (const [ax, ay] of dirs) {
const nx = x + ax, ny = y + ay;
if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] === 1) {
grid[nx][ny] = label;
stack.push([nx, ny]);
}
}
}
area.set(label, size);
label++;
}
}
}
let best = Math.max(0, ...area.values());
for (let r = 0; r < n; r++) {
for (let c = 0; c < n; c++) {
if (grid[r][c] === 0) {
const ids = new Set<number>();
for (const [ax, ay] of dirs) {
const nx = r + ax, ny = c + ay;
if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] > 1) {
ids.add(grid[nx][ny]);
}
}
let s = 1;
for (const i of ids) s += area.get(i)!;
best = Math.max(best, s);
}
}
}
return best;
}其他解法
搜索标号直观、常数小;并查集把连通块维护交给通用数据结构,支持增量合并。两者的第二遍枚举完全相同。
解法 2:并查集 时间 O(n²·α(n²)),空间 O(n²)
把格子 (r, c) 编号为 r·n + c。第一遍把所有相邻的陆地格子合并,按大小合并并在根上记录连通块面积。第二遍枚举每个海洋格子,取四个邻居中陆地格子的根并去重,把这些根的面积加起来再加 1,取最大值。全是陆地时答案是 n²。
不需要显式搜索,也不修改 grid,并查集的根本身就是岛屿编号。代码比搜索标号稍长,常数也略大,但在格子动态变成陆地的变体题中更通用。
class Solution:
def largestIsland(self, grid: List[List[int]]) -> int:
n = len(grid)
parent = list(range(n * n))
size = [1] * (n * n)
def find(x):
while parent[x] != x:
parent[x] = parent[parent[x]]
x = parent[x]
return x
def union(a, b):
ra, rb = find(a), find(b)
if ra == rb:
return
if size[ra] < size[rb]:
ra, rb = rb, ra
parent[rb] = ra
size[ra] += size[rb]
for r in range(n):
for c in range(n):
if grid[r][c] == 1:
if r + 1 < n and grid[r + 1][c] == 1:
union(r * n + c, (r + 1) * n + c)
if c + 1 < n and grid[r][c + 1] == 1:
union(r * n + c, r * n + c + 1)
best = 0
has_water = False
for r in range(n):
for c in range(n):
if grid[r][c] == 1:
best = max(best, size[find(r * n + c)])
continue
has_water = True
roots = set()
for nr, nc in ((r + 1, c), (r - 1, c), (r, c + 1), (r, c - 1)):
if 0 <= nr < n and 0 <= nc < n and grid[nr][nc] == 1:
roots.add(find(nr * n + nc))
best = max(best, 1 + sum(size[x] for x in roots))
return best if has_water else n * nclass Solution {
int[] parent, size;
int find(int x) {
while (parent[x] != x) {
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
void union(int a, int b) {
int ra = find(a), rb = find(b);
if (ra == rb) return;
if (size[ra] < size[rb]) {
int t = ra;
ra = rb;
rb = t;
}
parent[rb] = ra;
size[ra] += size[rb];
}
public int largestIsland(int[][] grid) {
int n = grid.length;
parent = new int[n * n];
size = new int[n * n];
for (int i = 0; i < n * n; i++) {
parent[i] = i;
size[i] = 1;
}
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
if (grid[r][c] != 1) continue;
if (r + 1 < n && grid[r + 1][c] == 1) union(r * n + c, (r + 1) * n + c);
if (c + 1 < n && grid[r][c + 1] == 1) union(r * n + c, r * n + c + 1);
}
}
int[] dr = {1, -1, 0, 0}, dc = {0, 0, 1, -1};
int best = 0;
boolean hasWater = false;
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
if (grid[r][c] == 1) {
best = Math.max(best, size[find(r * n + c)]);
continue;
}
hasWater = true;
Set<Integer> roots = new HashSet<>();
for (int d = 0; d < 4; d++) {
int nr = r + dr[d], nc = c + dc[d];
if (nr >= 0 && nr < n && nc >= 0 && nc < n && grid[nr][nc] == 1) roots.add(find(nr * n + nc));
}
int total = 1;
for (int root : roots) total += size[root];
best = Math.max(best, total);
}
}
return hasWater ? best : n * n;
}
}class Solution {
public:
vector<int> parent, sz;
int root(int x) {
while (parent[x] != x) {
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
void unite(int a, int b) {
int ra = root(a), rb = root(b);
if (ra == rb) return;
if (sz[ra] < sz[rb]) swap(ra, rb);
parent[rb] = ra;
sz[ra] += sz[rb];
}
int largestIsland(vector<vector<int>>& grid) {
int n = grid.size();
parent.resize(n * n);
sz.assign(n * n, 1);
for (int i = 0; i < n * n; i++) parent[i] = i;
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
if (grid[r][c] != 1) continue;
if (r + 1 < n && grid[r + 1][c] == 1) unite(r * n + c, (r + 1) * n + c);
if (c + 1 < n && grid[r][c + 1] == 1) unite(r * n + c, r * n + c + 1);
}
}
int dr[4] = {1, -1, 0, 0}, dc[4] = {0, 0, 1, -1};
int best = 0;
bool hasWater = false;
for (int r = 0; r < n; r++) {
for (int c = 0; c < n; c++) {
if (grid[r][c] == 1) {
best = max(best, sz[root(r * n + c)]);
continue;
}
hasWater = true;
vector<int> roots;
for (int d = 0; d < 4; d++) {
int nr = r + dr[d], nc = c + dc[d];
if (nr < 0 || nr >= n || nc < 0 || nc >= n || grid[nr][nc] != 1) continue;
int rt = root(nr * n + nc);
if (find(roots.begin(), roots.end(), rt) == roots.end()) roots.push_back(rt);
}
int total = 1;
for (int r2 : roots) total += sz[r2];
best = max(best, total);
}
}
return hasWater ? best : n * n;
}
};func largestIsland(grid [][]int) int {
n := len(grid)
parent := make([]int, n*n)
size := make([]int, n*n)
for i := range parent {
parent[i] = i
size[i] = 1
}
var find func(x int) int
find = func(x int) int {
for parent[x] != x {
parent[x] = parent[parent[x]]
x = parent[x]
}
return x
}
union := func(a, b int) {
ra, rb := find(a), find(b)
if ra == rb {
return
}
if size[ra] < size[rb] {
ra, rb = rb, ra
}
parent[rb] = ra
size[ra] += size[rb]
}
for r := 0; r < n; r++ {
for c := 0; c < n; c++ {
if grid[r][c] != 1 {
continue
}
if r+1 < n && grid[r+1][c] == 1 {
union(r*n+c, (r+1)*n+c)
}
if c+1 < n && grid[r][c+1] == 1 {
union(r*n+c, r*n+c+1)
}
}
}
dirs := [][2]int{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}
best, hasWater := 0, false
for r := 0; r < n; r++ {
for c := 0; c < n; c++ {
if grid[r][c] == 1 {
best = max(best, size[find(r*n+c)])
continue
}
hasWater = true
roots := map[int]bool{}
for _, d := range dirs {
nr, nc := r+d[0], c+d[1]
if nr >= 0 && nr < n && nc >= 0 && nc < n && grid[nr][nc] == 1 {
roots[find(nr*n+nc)] = true
}
}
total := 1
for root := range roots {
total += size[root]
}
best = max(best, total)
}
}
if !hasWater {
return n * n
}
return best
}var largestIsland = function(grid) {
const n = grid.length;
const parent = Array.from({ length: n * n }, (_, i) => i);
const size = new Array(n * n).fill(1);
const find = (x) => {
while (parent[x] !== x) {
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
};
const union = (a, b) => {
let ra = find(a), rb = find(b);
if (ra === rb) return;
if (size[ra] < size[rb]) [ra, rb] = [rb, ra];
parent[rb] = ra;
size[ra] += size[rb];
};
for (let r = 0; r < n; r++) {
for (let c = 0; c < n; c++) {
if (grid[r][c] !== 1) continue;
if (r + 1 < n && grid[r + 1][c] === 1) union(r * n + c, (r + 1) * n + c);
if (c + 1 < n && grid[r][c + 1] === 1) union(r * n + c, r * n + c + 1);
}
}
const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];
let best = 0, hasWater = false;
for (let r = 0; r < n; r++) {
for (let c = 0; c < n; c++) {
if (grid[r][c] === 1) {
best = Math.max(best, size[find(r * n + c)]);
continue;
}
hasWater = true;
const roots = new Set();
for (const [dr, dc] of dirs) {
const nr = r + dr, nc = c + dc;
if (nr >= 0 && nr < n && nc >= 0 && nc < n && grid[nr][nc] === 1) roots.add(find(nr * n + nc));
}
let total = 1;
for (const root of roots) total += size[root];
best = Math.max(best, total);
}
}
return hasWater ? best : n * n;
};function largestIsland(grid: number[][]): number {
const n = grid.length;
const parent: number[] = Array.from({ length: n * n }, (_, i) => i);
const size: number[] = new Array(n * n).fill(1);
const find = (x: number): number => {
while (parent[x] !== x) {
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
};
const union = (a: number, b: number): void => {
let ra = find(a), rb = find(b);
if (ra === rb) return;
if (size[ra] < size[rb]) [ra, rb] = [rb, ra];
parent[rb] = ra;
size[ra] += size[rb];
};
for (let r = 0; r < n; r++) {
for (let c = 0; c < n; c++) {
if (grid[r][c] !== 1) continue;
if (r + 1 < n && grid[r + 1][c] === 1) union(r * n + c, (r + 1) * n + c);
if (c + 1 < n && grid[r][c + 1] === 1) union(r * n + c, r * n + c + 1);
}
}
const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];
let best = 0, hasWater = false;
for (let r = 0; r < n; r++) {
for (let c = 0; c < n; c++) {
if (grid[r][c] === 1) {
best = Math.max(best, size[find(r * n + c)]);
continue;
}
hasWater = true;
const roots = new Set<number>();
for (const [dr, dc] of dirs) {
const nr = r + dr, nc = c + dc;
if (nr >= 0 && nr < n && nc >= 0 && nc < n && grid[nr][nc] === 1) roots.add(find(nr * n + nc));
}
let total = 1;
for (const root of roots) total += size[root];
best = Math.max(best, total);
}
}
return hasWater ? best : n * n;
}