精品推荐:
这题是谷歌和雅虎的一道经典面试题:你有 5 个药罐,每个药罐有 4 个药丸,每个药丸重 10 克,其中有一个罐子受到了污染,污染的罐子中每个药丸重 9 克。给你一个秤,如何仅通过一次测量就判断出哪个罐子里的药丸被污染了?
思路:
题中说了只需要一次测量,可以从 1 号罐取出 0 粒药丸,2 号罐取出 1 粒药丸,3 号罐取出 2 粒药丸……
总共取出 0+1+2+3+4 = 10 粒药丸,如果重 100g,说明是 1 号罐受到了污染,如果重 99g ,说明是 2 号罐受到了污染,如果重 98g ,说明 3 号罐受到了污染……。
--------------下面是今天的算法题--------------
来看下今天的算法题,这题是LeetCode的第827题:最大人工岛。
问题描述
来源:LeetCode第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。
n == grid.length
n == grid[i].length
1 <= n <= 500
grid[i][j] 为 0 或 1
问题分析
这题说的是最多只能将一个 0 变成 1 ,然后求最大的岛屿面积,和我们昨天讲的岛屿的最大面积类似。
我们可以按照之前的方式先计算所有岛屿的面积,然后尝试把 0 变成 1 ,计算最大面积。因为 0 变成的 1 的时候,两个本来不相连的岛屿可能会相连,岛屿的面积就会增大,如下图所示。
所以在计算完岛屿面积之后,需要再遍历一次二维数组,如果是 0 ,就尝试把它变成 1 ,然后把它的上下左右 4 个方向有岛屿的连在一起计算面积。
为了连接的时候防止岛屿有重复,我们需要把每个岛屿编上号,如果号码是一样的,说明他们是同一个岛屿,不能连接,如下图所示:
JAVA:
// 每一个岛屿的区域标记为一个不同的数字
public int largestIsland(int[][] grid) {
int length = grid.length;
Map<Integer, Integer> mp = new HashMap<>();
int ans = 0;// 保存最大的岛屿
int landNo = 2;// 岛屿编号从 2 开始
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
if (grid[i][j] == 1) {
int area = dfs(grid, i, j, length, landNo);
mp.put(landNo++, area);// 保存到map中,岛屿编号也要累加
ans = Math.max(ans, area);
}
}
}
Set<Integer> mySet = new HashSet<>();// 去掉重复的
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
// 如果遇到0,尝试把它的上下左右连接起来,保存最大面积。
if (grid[i][j] == 0) {
int up = i > 0 ? grid[i - 1][j] : 0;
int down = i < length - 1 ? grid[i + 1][j] : 0;
int left = j > 0 ? grid[i][j - 1] : 0;
int right = j < length - 1 ? grid[i][j + 1] : 0;
// 上下左右在连接之前肯定是不能相连的,如果是相连的,只能取一个
mySet.clear();
mySet.addAll(Arrays.asList(up, down, left, right));
int tmp = 0;
for (int s : mySet) {
if (s != 0)
tmp += mp.get(s);
}
ans = Math.max(ans, tmp + 1);
}
}
}
return ans;
}
// 通过dfs遍历当前位置的上下左右四个方向
private int dfs(int[][] grid, int i, int j, int length, int landNo) {
// 越界处理
if (i < 0 || i >= length || j < 0 || j >= length || grid[i][j] != 1)
return 0;
int res = 1;
grid[i][j] = landNo;// 相连的标记为同一个岛屿
res += dfs(grid, i - 1, j, length, landNo);// 上
res += dfs(grid, i + 1, j, length, landNo);// 下
res += dfs(grid, i, j - 1, length, landNo);// 左
res += dfs(grid, i, j + 1, length, landNo);// 右
return res;
}
C++:
public:
int largestIsland(vector<vector<int>> &grid) {
int length = grid.size();
unordered_map<int, int> mp;
int ans = 0;// 保存最大的岛屿
// 每一个岛屿的区域标记为一个不同的数字
int landNo = 2;// 岛屿编号从 2 开始
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
if (grid[i][j] == 1) {
int area = dfs(grid, i, j, length, landNo);
mp[landNo++] = area;// 保存到map中,岛屿编号也要累加
ans = max(ans, area);
}
}
}
unordered_set<int> mySet;// 去掉重复的
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
// 如果遇到0,尝试把它的上下左右连接起来,保存最大面积。
if (grid[i][j] == 0) {
int up = i > 0 ? grid[i - 1][j] : 0;
int down = i < length - 1 ? grid[i + 1][j] : 0;
int left = j > 0 ? grid[i][j - 1] : 0;
int right = j < length - 1 ? grid[i][j + 1] : 0;
// 上下左右在连接之前肯定是不能相连的,如果是相连的,只能取一个
mySet.clear();
mySet.insert(up);
mySet.insert(down);
mySet.insert(left);
mySet.insert(right);
int tmp = 0;
for (int s: mySet) {
if (s != 0)
tmp += mp[s];
}
ans = max(ans, tmp + 1);
}
}
}
return ans;
}
// 通过dfs遍历当前位置的上下左右四个方向
int dfs(vector<vector<int>> &grid, int i, int j, int length, int landNo) {
// 越界处理
if (i < 0 || i >= length || j < 0 || j >= length || grid[i][j] != 1)
return 0;
int res = 1;
grid[i][j] = landNo;// 相连的标记为同一个岛屿
res += dfs(grid, i - 1, j, length, landNo);// 上
res += dfs(grid, i + 1, j, length, landNo);// 下
res += dfs(grid, i, j - 1, length, landNo);// 左
res += dfs(grid, i, j + 1, length, landNo);// 右
return res;
}
Python:
def largestIsland(self, grid: List[List[int]]) -> int:
# 通过dfs遍历当前位置的上下左右四个方向
def dfs(i, j, length, landNo):
# 越界处理
if i < 0 or i >= length or j < 0 or j >= length or grid[i][j] != 1:
return 0
res = 1
grid[i][j] = landNo # 相连的标记为同一个岛屿
res += dfs(i - 1, j, length, landNo) # 上
res += dfs(i + 1, j, length, landNo) # 下
res += dfs(i, j - 1, length, landNo) # 左
res += dfs(i, j + 1, length, landNo) # 右
return res
length = len(grid)
mp = {}
ans = 0 # 保存最大的岛屿
landNo = 2 # 岛屿编号从 2 开始
for i in range(length):
for j in range(length):
if grid[i][j] == 1:
area = dfs(i, j, length, landNo)
mp[landNo] = area # 保存到map中,岛屿编号也要累加
ans = max(ans, area)
landNo += 1
for i in range(length):
for j in range(length):
# 如果遇到0,尝试把它的上下左右连接起来,保存最大面积。
if grid[i][j] == 0:
up = grid[i - 1][j] if i > 0 else 0
down = grid[i + 1][j] if i < length - 1 else 0
left = grid[i][j - 1] if j > 0 else 0
right = grid[i][j + 1] if j < length - 1 else 0
# 上下左右在连接之前肯定是不能相连的,如果是相连的,只能取一个
mySet = {up, down, left, right} # 去掉重复的
tmp = 0
for s in mySet:
if s != 0:
tmp += mp[s]
ans = max(ans, tmp + 1)
return ans
笔者简介
博哥,真名:王一博,毕业十多年,《算法秘籍》作者,专注于数据结构和算法的讲解,在全球30多个算法网站中累计做题2000多道,在公众号中写算法题解800多题,对算法题有自己独特的解题思路和解题技巧,喜欢的可以给个关注,也可以下载我整理的1000多页的PDF算法文档。