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);
        }
    }
};
posted on 2021-05-11 21:28  雾恋过往  阅读(60)  评论(0编辑  收藏  举报

Live2D