❓ 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
💡思路:
本题的一个暴力想法,应该是遍历地图尝试 将每一个
0
改成1
,然后去搜索地图中的最大的岛屿面积。
本题使用 深搜 还是 广搜 都是可以的,其目的就是遍历岛屿做一个标记,相当于染色,那么使用哪个遍历方式都行,我用的是深搜。
根据暴力的想法每次深搜遍历计算最大岛屿面积,我们都做了很多重复的工作。只要用一次深搜把每个岛屿的面积记录下来就好。
- 一次遍历地图,得出各个岛屿的面积,并做编号记录。可以使用
map
记录,key
为岛屿编号,value
为岛屿面积,我是直接在原grid
上直接标记; - 在遍历地图,遍历
0
的方格(因为要将0
变成1
),并统计该1
(由0
变成的1)周边岛屿面积,将其相邻面积相加在一起,遍历所有0
之后,就可以得出 选一个0
变成1
之后的最大面积。
🍁代码:(C++、Java)
C++
class Solution {
private:
int cnt;
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
void dfs(vector<vector<int>>& grid, int x, int y, int mark){
if(grid[x][y] == 0 || grid[x][y] != 1) return; // 遇到海水或访问过的节点,则终止
grid[x][y] = mark; //给陆地标记新的标签
cnt++;
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, nextx, nexty, mark);
}
}
public:
int largestIsland(vector<vector<int>>& grid) {
int m = grid.size(), n = grid[0].size();
unordered_map<int, int> gridNum;
int mark = 2;
bool isAllGrid = true; // 标记是否整个地图都是陆地
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(grid[i][j] == 0) isAllGrid = false;
if(grid[i][j] == 1){
cnt = 0;
dfs(grid, i, j, mark);
gridNum[mark] = cnt;
mark++;
}
}
}
if (isAllGrid) return n * m; // 如果都是陆地,返回全面积
// 以下逻辑是根据添加陆地的位置,计算周边岛屿面积之和
int ans = 0;
unordered_set<int> visitedGrid; // 标记访问过的岛屿
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
int num = 1;
visitedGrid.clear(); // 每次使用时,清空
if(grid[i][j] == 0){
for(int k = 0; k < 4; k++){
int neari = i + dir[k][0];
int nearj = j + dir[k][1];
if (neari < 0 || neari >= grid.size() || nearj < 0 || nearj >= grid[0].size()) continue;
if (visitedGrid.count(grid[neari][nearj])) continue;
// 把相邻四面的岛屿数量加起来
num += gridNum[grid[neari][nearj]];
visitedGrid.insert(grid[neari][nearj]); // 标记该岛屿已经添加
}
}
ans = max(ans, num);
}
}
return ans;
}
};
Java
class Solution {
private static final int[][] dir = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; // 四个方向
private int cnt;
private void dfs(int[][] grid, int x, int y, int mark){
if(grid[x][y] != 1) return;
grid[x][y] = mark;
cnt++;
for(int i = 0; i < 4; i++){
int nextx = x + dir[i][0];
int nexty = y + dir[i][1];
if(nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length) continue;
dfs(grid, nextx, nexty, mark);
}
}
public int largestIsland(int[][] grid) {
int m = grid.length, n = grid[0].length;
Map<Integer, Integer> gridNum = new HashMap<>();
int mark = 2;
boolean isAllGrid = true;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(grid[i][j] == 0) isAllGrid = false;
if(grid[i][j] == 1){
cnt = 0;
dfs(grid, i, j, mark);
gridNum.put(mark++, cnt);
}
}
}
if(isAllGrid) return n * m;
int ans = 0;
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
Set<Integer> hashSet = new HashSet<>(); // 防止同一个区域被重复计算
int num = 1;
if(grid[i][j] == 0){
for(int k = 0; k < 4; k++){
int neari = i + dir[k][0];
int nearj = j + dir[k][1];
if(neari < 0 || neari >= grid.length || nearj < 0 || nearj >= grid[0].length) continue;
int curMark = grid[neari][nearj]; // 获取对应位置的标记
if (hashSet.contains(curMark) || !gridNum.containsKey(curMark)) continue;
num += gridNum.get(curMark);
hashSet.add(curMark);
}
ans = Math.max(ans, num);
}
}
}
return ans;
}
}
🚀 运行结果:
🕔 复杂度分析:
- 时间复杂度:
O
(
m
n
)
O(mn)
O(mn),其中
m
和n
为grid
的高和宽。方格地图中,每个节点我们就遍历一次,并不会重复遍历。 - 空间复杂度: O ( m n ) O(mn) O(mn)。
题目来源:力扣。
放弃一件事很容易,每天能坚持一件事一定很酷,一起每日一题吧!
关注我LeetCode主页 / CSDN—力扣专栏,每日更新!