LeetCode200. 岛屿问题

题目描述

给你一个由 ‘1’(陆地)和 ‘0’(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。

涉及tag

dfs;bfs;并查集

网格问题

网格问题类比二叉树问题,下面给出网格问题的框架代码:

void dfs(int[][] grid, int r, int c) {
    // 判断退出递归条件
    // 如果坐标 (r, c) 超出了网格范围,直接返回
    if (!inArea(grid, r, c)) {
        return;
    }
    // 访问上、下、左、右四个相邻结点
    dfs(grid, r - 1, c);
    dfs(grid, r + 1, c);
    dfs(grid, r, c - 1);
    dfs(grid, r, c + 1);
}

// 判断坐标 (r, c) 是否在网格中
boolean inArea(int[][] grid, int r, int c) {
    return 0 <= r && r < grid.length 
        	&& 0 <= c && c < grid[0].length;
}

算法思路

用经典dfs模板,注意两个特殊判断
也可以用bfs,遍历到res++
并查集:
将每个标注为1的格子看成是一个岛屿,使用并查集进行连通分量的合并,记录合并次数,最后用总数减去合并次数就是岛屿的数量。
需要注意初始化时要将二维grid[i][j]映射到一维parents[k]中,k = i * COL + j,COL是grid的列数。初始化parents: grid[i][j] = 1时 parents[k] = k,size[k] = 1。

示例代码1(dfs)

为避免重复遍历,设置状态变量grid[r][c] = 2;

class Solution {
    public int numIslands(char[][] grid) {
        int res = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
            //把该网格周围的所有网格都遍历到,才能新增一个岛屿数量
                if (grid[i][j] == '1') {
                    dfs(grid, i, j);
                    res++;
                }
            }
        }
        return res;
    }
    public void dfs(char[][] grid, int row, int col) {
        if (row < 0 || col < 0 || row >= grid.length || col >= grid[0].length) return;
        if (grid[row][col] != '1')
        return;
        grid[row][col] = '2';
        dfs(grid, row + 1, col);
        dfs(grid, row - 1, col);
        dfs(grid, row, col + 1);
        dfs(grid, row, col - 1);
    }
}

示例代码2(bfs)

class Solution {
    public int numIslands(char[][] grid) {
        int count = 0;
        for(int i = 0; i < grid.length; i++) {
            for(int j = 0; j < grid[0].length; j++) {
                if(grid[i][j] == '1'){
                    bfs(grid, i, j);
                    count++;
                }
            }
        }
        return count;
    }
    //把找到的每一个岛屿都变成海洋,再找下一个
    private void bfs(char[][] grid, int i, int j){
        Queue<int[]> list = new LinkedList<>();
        list.add(new int[] { i, j });
        while(!list.isEmpty()){
            int[] cur = list.remove();
            i = cur[0]; 
            j = cur[1];
            if(0 <= i && i < grid.length && 0 <= j && j < grid[0].length && grid[i][j] == '1') {
                grid[i][j] = '0';
                list.add(new int[] { i + 1, j });
                list.add(new int[] { i - 1, j });
                list.add(new int[] { i, j + 1 });
                list.add(new int[] { i, j - 1 });
            }
        }
    }
}

示例代码3

class Solution {
    int islandsCount = 0;
    int mergedCount = 0;
    public int numIslands(char[][] grid) {
        int m = grid.length, n = grid[0].length;
        UnionFind uf = new UnionFind(grid);
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                int landx = i * n + j;
                if(grid[i][j] == '1'){ // 检测右侧和下侧是否连通,连通时合并之(用并列的if,在union中会检测是否属于同一集合)
                    if(j < n - 1 && grid[i][j + 1] == '1') { // 右侧
                        uf.union(landx, landx + 1);
                    }
                    if(i < m - 1 && grid[i + 1][j] == '1') { // 下侧
                        uf.union(landx, landx + n);
                    }
                }
            }
        }
        return islandsCount - mergedCount;
    }

    private class UnionFind{
        private int[] parents;
        private int[] rank;

        public UnionFind(char[][] grid){
            int m = grid.length, n = grid[0].length;
            parents = new int[m * n];
            rank = new int[m * n];
            for(int i = 0; i < m; i++){
                for(int j = 0; j < n; j++) {
                    if(grid[i][j] == '1'){ // 针对单格岛,累计岛屿数量,赋值parents[k] rank[k]
                        islandsCount++;
                        int k = i * n + j;
                        parents[k] = k;
                        rank[k] = 1;
                    }
                }
            }
        }
        // 带路径压缩的查找
        public int find(int x){
            if(parents[x] == x) return x;
            return parents[x] = find(parents[x]);
        }
        // 按秩合并
        public void union(int x, int y){
            int xRoot = find(x);
            int yRoot = find(y);
            if(xRoot != yRoot){
                mergedCount++; // 只有不属于一个集合时,合并才次数加1
                if(rank[yRoot] <= rank[xRoot]) parents[yRoot] = xRoot;// yRoot挂到xRoot上
                else parents[xRoot] = yRoot; // xRoot挂到yRoot上
                if (rank[xRoot] == rank[yRoot]) rank[xRoot]++; // 秩相同时才加1
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值