1254. 统计封闭岛屿的数目
题目:
有一个二维矩阵 grid ,每个位置要么是陆地(记号为 0 )要么是水域(记号为 1 )。我们从一块陆地出发,每次可以往上下左右 4 个方向相邻区域走,能走到的所有陆地区域,我们将其称为一座「岛屿」。如果一座岛屿 完全 由水域包围,即陆地边缘上下左右所有相邻区域都是水域,那么我们将其称为 「封闭岛屿」。请返回封闭岛屿的数目。
换句话说就是从当前陆地走,你走不到数组边界的陆地都是封闭岛屿(或封闭岛屿的一块陆地)
思路:
从当前陆地开始出发,如果能走出边界就说明该陆地所在岛屿不是封闭岛屿,返回封闭岛屿个数0,如果碰到水域(值为1的点)就返回封闭岛屿个数1,表示该岛屿可能就是一个封闭岛屿,如果碰到陆地(值为0的点)就继续向该陆地的四个方向遍历,同时将该陆地标记为1,表示这个位置已经遍历过了。
class Solution {
public:
int closedIsland(vector<vector<int>>& grid) {
int ret = 0; //ret为最后的封闭岛屿数
for (int i = 0; i < grid.size(); i++) { //遍历所有的点
for (int j = 0; j < grid[0].size(); j++) {
if (grid[i][j] == 0) { //只要是陆地就开始dfs
ret += dfs(grid, i, j);
}
}
}
return ret;
}
int dfs(vector<vector<int>>& grid, int r, int c) { //传引用是因为我们遍历完一个点后就要把它做个标记,以免在目标函数中重复遍历
if (r < 0 || r >= grid.size() || r < 0 || c >= grid[0].size()) { //如果遍历出边界了说明没有全部被水包围,不是封闭岛屿
return 0;
}
if (grid[r][c] == 1) { //该点是水就返回1,代表前一个点到该点的这个方向被水包围
return 1;
}
grid[r][c] = 1; //做个标记,也可以置为-1或者其他非0的数。如果改成-1,则在前面一个if里要判断是不是标记数,是的话也返回1
int vr[] = {0, 1, 0, -1}; //利用两个数组加一个循环来代替写几个dfs。
int vc[] = {1, 0, -1, 0};
int ret = 1;
for (int i = 0; i < 4; i++) {
ret = min(ret, dfs(grid, r+vr[i], c+vc[i])); //取最小是因为要判断是不是有返回0(即走出边界的情况)
}
return ret; //ret不是0就是1,0代表从这个点出发不是封闭岛屿,不是封闭岛屿意味着与他相连的陆地也不是封闭肚的,1代表是封闭岛屿
}
};
说明一下以下两个是等价的,用循环代替的原因是万一说可以走八个方向,循环还是三行,但直接写dfs的话太多了。
int vr[] = {0, 1, 0, -1}; //利用两个数组加一个循环来代替写几个dfs。
int vc[] = {1, 0, -1, 0};
for (int i = 0; i < 4; i++) {
dfs(grid, r+vr[i], c+vc[i]);
}
dfs(grid, r, c+1);
dfs(grid, r+1, c);
dfs(grid, r-1, c);
dfs(grid, r, c-1);
思路二:
1、其实我们还可以直接从边界的陆地开始DFS或BFS遍历,只要边界陆地能遍历到的地方就不是封闭岛屿。
2、同时我们也要将遍历过得点置为1,表示该位置已经遍历过。
3、最后,里面为0的位置都是属于封闭岛屿的陆地了。
class Solution {
public:
int closedIsland(vector<vector<int>>& grid) {
int ret = 0;
int ylen = grid.size();
int xlen = grid[0].size();
//把从边界的陆地能走到的都先标记为水域(标记为1)
for (int i = 0; i < ylen; i++) {
for (int j = 0; j < xlen; j++) {
if (i == 0 || j == 0 || i == ylen-1 || j == xlen-1) {
dfs(j, i, grid);
}
}
}
//剩下的就都是属于封闭岛屿的陆地了
for (int i = 0; i < ylen; i++) {
for (int j = 0; j < xlen; j++) {
if (grid[i][j] == 0) {
ret++;
dfs(j, i, grid);
}
}
}
return ret;
}
void dfs(int x, int y, vector<vector<int>>& grid) { //这个dfs只是为了把从点[x,y]开始的可以走到的陆地都走一遍,然后置1标记走过了
int xlen = grid[0].size();
int ylen = grid.size();
if (x >= xlen || y >= ylen || x < 0 || y < 0 || grid[y][x] == 1) {
return;
}
grid[y][x] = 1; //标记这个点走过了
int vx[] = {0, 1, 0, -1};
int vy[] = {1, 0, -1, 0};
for (int i = 0; i < 4; i++) {
dfs(x+vx[i], y+vy[i], grid);
}
}
};