飞地的数量

题目描述

给你一个大小为 m x n 的二进制矩阵 grid ,其中 0 表示一个海洋单元格、1 表示一个陆地单元格。

一次 移动 是指从一个陆地单元格走到另一个相邻(上、下、左、右)的陆地单元格或跨过 grid 的边界。

返回网格中 无法 在任意次数的移动中离开网格边界的陆地单元格的数量。

飞地的数量_第1张图片

思路

首先我们要处理边界问题,因为题中告诉我们,边界的陆地不在计算范围内,所以我们第一步要把四个边界的陆地做处理,也就是把陆地变海洋。代码如下:

// 处理四个边界
for(int i = 0;i < m;i++){
    // 左
    if(grid[i][0] == 1) dfs(grid, i, 0, m, n);
    // 右
    if(grid[i][n - 1] == 1) dfs(grid, i, n - 1, m, n);
}
for(int i = 0;i < n;i++){
    // 上
    if(grid[0][i] == 1) dfs(grid, 0, i, m, n);
    //下
    if(grid[m - 1][i] == 1) dfs(grid, m - 1, i, m, n);
}

接下来的代码就是上次我们讲的岛屿的数量代码,稍微变动即可。

回溯代码如下:

public void dfs(int[][] grid, int i, int j, int m, int n){
    if(i >=m || i < 0 || j >= n || j < 0 || grid[i][j] == 0){
        return;
    }
    grid[i][j] = 0;
    count++;
    dfs(grid, i + 1, j, m, n);
    dfs(grid, i - 1, j, m, n);
    dfs(grid, i, j + 1, m, n);
    dfs(grid, i, j - 1, m, n);
}

整体代码如下:

public class NumEnclaves {

    int count = 0;

    /**
     * 解决思路和Solve一样,都是从四个边开始出发,将与边界相连的1都置为0,之后,再重新计算1的数量即可
     * @param grid
     * @return
     */
    public int numEnclavesWithDfs(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        for(int i = 0;i < m;i++){
            if(grid[i][0] == 1) dfs(grid, i, 0, m, n);
            if(grid[i][n - 1] == 1) dfs(grid, i, n - 1, m, n);
        }
        for(int i = 0;i < n;i++){
            if(grid[0][i] == 1) dfs(grid, 0, i, m, n);
            if(grid[m - 1][i] == 1) dfs(grid, m - 1, i, m, n);
        }
        count = 0;
        for(int i = 0;i < m;i++){
            for(int j = 0;j < n;j++){
                if(grid[i][j] == 1){
                    dfs(grid, i, j, m, n);
                }
            }
        }
        return count;
    }

    public void dfs(int[][] grid, int i, int j, int m, int n){

        if(i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == 0) return;
        grid[i][j] = 0;
        count++;
        dfs(grid, i, j + 1, m, n);
        dfs(grid, i, j - 1, m, n);
        dfs(grid, i - 1, j, m, n);
        dfs(grid, i + 1, j, m, n);
    }
}

上面的代码非常耗时,主要是递归会遍历重复的,我们加上标记位置,只要我们遍历过该位置,就把该位置进行标记,下次遍历时,只要该点遍历过,直接跳过即可。代码如下:

public class NumEnclaves {
	boolean[][] visited;
	int count = 0;
    public int numEnclaves(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        visited = new boolean[m][n];
        for(int i = 0;i < m;i++){
            if(grid[i][0] == 1) dfsWithVisited(grid, i, 0, m, n);
            if(grid[i][n - 1] == 1) dfsWithVisited(grid, i, n - 1, m, n);
            
        }
        for(int i = 0;i < n;i++){
            if(grid[0][i] == 1) dfsWithVisited(grid, 0, i, m, n);
            if(grid[m - 1][i] == 1) dfsWithVisited(grid, m - 1, i, m, n);
        }
        for(int i = 0;i < m;i++){
            for(int j = 0;j < n;j++){
                if(grid[i][j] == 1 && !visited[i][j]){
                    count++;
                }
            }
        }
        return count;
    }

    public void dfsWithVisited(int[][] grid, int i, int j, int m, int n){
        
        if(i < 0 || i >= m || j < 0 || j >= n || grid[i][j] == 0) return;
        grid[i][j] = 0;
        visited[i][j] = true;
        dfsWithVisited(grid, i, j + 1, m, n);
        dfsWithVisited(grid, i, j - 1, m, n);
        dfsWithVisited(grid, i - 1, j, m, n);
        dfsWithVisited(grid, i + 1, j, m, n);
    }
}

你可能感兴趣的:(回溯,java)