「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战」。
题目
给你一个大小为 m x n 的二进制矩阵 grid ,其中 0 表示一个海洋单元格、1 表示一个陆地单元格。
一次 移动 是指从一个陆地单元格走到另一个相邻(上、下、左、右)的陆地单元格或跨过 grid 的边界。
返回网格中 无法 在任意次数的移动中离开网格边界的陆地单元格的数量。
示例 1:
输入:grid = [[0,0,0,0],[1,0,1,0],[0,1,1,0],[0,0,0,0]]
输出:3
解释:有三个 1 被 0 包围。一个 1 没有被包围,因为它在边界上。
示例 2:
输入:grid = [[0,1,1,0],[0,0,1,0],[0,0,1,0],[0,0,0,0]]
输出:0
解释:所有 1 都在边界上或可以到达边界。
思路
先理解题意,题目要求的是飞地的数量,就是不能连通到边界的陆地数量。吐槽一下,其实从“飞地的数量”这个名字不难理解,但是给出的解释感觉反而让人难以理解了。
所以,本题其实要求的是,每个陆地跟边界陆地的连通性,第一反应是并查集,不过本题也可以用广度优先遍历的方法:我们以4条边界上的陆地点作为根,每个根需要遍历可能存在的联通的4个节点,然后如果存在联通的陆地,那么新的陆地也要放到根里面去,后面参加遍历。
注意点是,需要有一个跟grid一样大小的boolean类型数组,标记一个陆地点跟边界的连通性,避免重复遍历浪费时间。
Java版本代码
class Solution {
public int numEnclaves(int[][] grid) {
int m = grid.length;
int n = grid[0].length;
boolean[][] canvisit = new boolean[m][n];
Queue<int[]> queue = new ArrayDeque<>();
for (int j = 0; j < n; j++) {
if (grid[0][j] == 1 && !canvisit[0][j]) {
canvisit[0][j] = true;
queue.add(new int[]{0, j});
}
if (grid[m-1][j] == 1 && !canvisit[m-1][j]) {
canvisit[m-1][j] = true;
queue.add(new int[]{m-1, j});
}
}
for (int i = 0; i < m; i++) {
if (grid[i][0] == 1 && !canvisit[i][0]) {
canvisit[i][0] = true;
queue.add(new int[]{i, 0});
}
if (grid[i][n-1] == 1 && !canvisit[i][n-1]) {
canvisit[i][n-1] = true;
queue.add(new int[]{i, n-1});
}
}
int[][] offsets = new int[][]{{-1,0},{1,0},{0,-1},{0,1}};
while (!queue.isEmpty()) {
int[] point = queue.poll();
for (int[] offset : offsets) {
int x = point[0] + offset[0];
int y = point[1] + offset[1];
if (x>=0 && x<m && y>=0 && y<n && grid[x][y]==1 && !canvisit[x][y]) {
canvisit[x][y] = true;
queue.add(new int[]{x, y});
}
}
}
int ans = 0;
for (int i = 1; i < m-1; i++) {
for (int j = 1; j < n-1; j++) {
if (grid[i][j] == 1 && !canvisit[i][j]) {
ans++;
}
}
}
return ans;
}
}