目录
题目描述
给你一个大小为 m x n 的二进制矩阵 grid 。
岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
岛屿的面积是岛上值为 1 的单元格的数目。
计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。
示例 1:
输入:grid = [
[0,0,1,0,0,0,0,1,0,0,0,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,1,1,0,1,0,0,0,0,0,0,0,0],
[0,1,0,0,1,1,0,0,1,0,1,0,0],
[0,1,0,0,1,1,0,0,1,1,1,0,0],
[0,0,0,0,0,0,0,0,0,0,1,0,0],
[0,0,0,0,0,0,0,1,1,1,0,0,0],
[0,0,0,0,0,0,0,1,1,0,0,0,0]
]
输出:6
解释:答案不应该是 11 ,因为岛屿只能包含水平或垂直这四个方向上的 1 。
示例 2:
输入:grid = [[0,0,0,0,0,0,0,0]]
输出:0
提示:
m == grid.length
n == grid[i].length
1 <= m, n <= 50
grid[i][j] 为 0 或 1
思路分析
网格题目一般采用深度优先或则广度优先的方法
代码实现
方法一深度优先搜索
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int ans = 0;
for (int i = 0; i != grid.length; ++i) {
for (int j = 0; j != grid[0].length; ++j) {
ans = Math.max(ans, dfs(grid, i, j));
}
}
return ans;
}
public int dfs(int[][] grid, int cur_i, int cur_j) {
if (cur_i < 0 || cur_j < 0 || cur_i == grid.length || cur_j == grid[0].length || grid[cur_i][cur_j] != 1) {
return 0;
}
grid[cur_i][cur_j] = 0;
//上下左右四个方向一般采用数据形式进行记录,是常用的技巧
int[] di = {0, 0, 1, -1};
int[] dj = {1, -1, 0, 0};
int ans = 1;
for (int index = 0; index != 4; ++index) {
int next_i = cur_i + di[index], next_j = cur_j + dj[index];
ans += dfs(grid, next_i, next_j);
}
return ans;
}
}
方法二:深度优先搜索 + 栈
先遍历完某个网格的上下左右四个方向,再从最后一个网格遍历上下左右四个方向依次类推
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int ans = 0;
for (int i = 0; i != grid.length; ++i) {
for (int j = 0; j != grid[0].length; ++j) {
int cur = 0;
//LinkedList既可以作为栈也可以作为队列
Deque<Integer> stacki = new LinkedList<Integer>();
Deque<Integer> stackj = new LinkedList<Integer>();
stacki.push(i);
stackj.push(j);
while (!stacki.isEmpty()) {
int cur_i = stacki.pop(), cur_j = stackj.pop();
if (cur_i < 0 || cur_j < 0 || cur_i == grid.length || cur_j == grid[0].length || grid[cur_i][cur_j] != 1) {
continue;
}
++cur;
grid[cur_i][cur_j] = 0;
int[] di = {0, 0, 1, -1};
int[] dj = {1, -1, 0, 0};
for (int index = 0; index != 4; ++index) {
int next_i = cur_i + di[index], next_j = cur_j + dj[index];
stacki.push(next_i);
stackj.push(next_j);
}
}
ans = Math.max(ans, cur);
}
}
return ans;
}
}
方法三:广度优先搜索
先遍历完某个网格的上下左右四个方向,再遍历上下左右的每个网格自己的上下左右所有网格依次类推
class Solution {
public int maxAreaOfIsland(int[][] grid) {
int ans = 0;
for (int i = 0; i != grid.length; ++i) {
for (int j = 0; j != grid[0].length; ++j) {
int cur = 0;
Queue<Integer> queuei = new LinkedList<Integer>();
Queue<Integer> queuej = new LinkedList<Integer>();
queuei.offer(i);
queuej.offer(j);
while (!queuei.isEmpty()) {
int cur_i = queuei.poll(), cur_j = queuej.poll();
if (cur_i < 0 || cur_j < 0 || cur_i == grid.length || cur_j == grid[0].length || grid[cur_i][cur_j] != 1) {
continue;
}
++cur;
grid[cur_i][cur_j] = 0;
int[] di = {0, 0, 1, -1};
int[] dj = {1, -1, 0, 0};
for (int index = 0; index != 4; ++index) {
int next_i = cur_i + di[index], next_j = cur_j + dj[index];
queuei.offer(next_i);
queuej.offer(next_j);
}
}
ans = Math.max(ans, cur);
}
}
return ans;
}
}
题目变形
给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。
示例 1:
输入:board = [["X","X","X","X"],["X","O","O","X"],["X","X","O","X"],["X","O","X","X"]] 输出:[["X","X","X","X"],["X","X","X","X"],["X","X","X","X"],["X","O","X","X"]] 解释:被围绕的区间不会存在于边界上,换句话说,任何边界上的'O'
都不会被填充为'X'
。 任何不在边界上,或不与边界上的'O'
相连的'O'
最终都会被填充为'X'
。如果两个元素在水平或垂直方向相邻,则称它们是“相连”的。
示例 2:
输入:board = [["X"]] 输出:[["X"]]
思路分析
从边界上的 'O' 开始,用深度优先搜索(DFS)找到所有与边界相连的 'O',并将其变为 '#'。遍历完边界上的 'O' 后,所有剩下的 'O' 都是被 'X' 包围的,都将其变为 'X'。最后,将 '#' 变回 'O'。
代码实现
方法一:深度优先
class Solution:
def solve(self, board: List[List[str]]) -> None:
if not board:
return
m, n = len(board), len(board[0])
def dfs(x, y):
if x < 0 or x >= m or y < 0 or y >= n or board[x][y] != 'O':
return
board[x][y] = '#'
dfs(x + 1, y)
dfs(x - 1, y)
dfs(x, y + 1)
dfs(x, y - 1)
for i in range(m):
dfs(i, 0)
dfs(i, n - 1)
for i in range(n):
dfs(0, i)
dfs(m - 1, i)
for i in range(m):
for j in range(n):
if board[i][j] == '#':
board[i][j] = 'O'
elif board[i][j] == 'O':
board[i][j] = 'X'
方法二:广度优先
算法的主要思路是:
-
首先,检查二维数组board的四个边界上是否存在'O'字符,如果存在,则将其变为'#',并将其坐标放入队列中。
-
接着,使用BFS方法,从队列中取出一个坐标,然后检查其上下左右四个方向的字符,如果是'O',则将其变为'#',并将该坐标放入队列中。重复此操作,直到队列为空。
-
最后,遍历整个二维数组,将所有的'#'变回'O',将所有的'O'变为'X'。这样,就能保证所有被'X'完全包围的'O'都被替换成了'X',而没有被包围的'O'则保持不变。
这种方法的主要思想是,从边界出发,找到所有与边界上的'O'相连的'O',这些'O'都不会被'X'包围,然后再将剩下的'O'都变为'X'。
from collections import deque
class Solution:
def solve(self, board: List[List[str]]) -> None:
if not board or not board[0]:
return
n, m = len(board), len(board[0])
que = deque()
# 标记边界上的 'O' 以及与边界 'O' 相连的 'O'
for i in range(n):
if board[i][0] == 'O':
que.append((i, 0))
board[i][0] = '#'
if board[i][m - 1] == 'O':
que.append((i, m - 1))
board[i][m - 1] = '#'
for i in range(1, m - 1):
if board[0][i] == 'O':
que.append((0, i))
board[0][i] = '#'
if board[n - 1][i] == 'O':
que.append((n - 1, i))
board[n - 1][i] = '#'
# BFS 遍历与边界相连的 'O',并标记为 '#'
while que:
x, y = que.popleft()
for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
mx, my = x + dx, y + dy
if 0 <= mx < n and 0 <= my < m and board[mx][my] == 'O':
que.append((mx, my))
board[mx][my] = '#'
# 遍历整个板,将剩余的 'O'(被围绕的区域)替换为 'X',将 '#'(与边界相连的区域)还原为 'O'
for i in range(n):
for j in range(m):
if board[i][j] == '#':
board[i][j] = 'O' # 还原与边界相连的 'O'
elif board[i][j] == 'O':
board[i][j] = 'X' # 将被围绕的 'O' 转换为 'X'