想要精通算法和SQL的成长之路 - 岛屿数量和岛屿的最大面积

想要精通算法和SQL的成长之路 - 岛屿数量和岛屿的最大面积

前言

想要精通算法和SQL的成长之路 - 系列导航
并查集的运用

一. 岛屿数量

原题链接

在这里插入图片描述

从这个题目的特性来看,它适合用并查集来解决。对并查集还不清楚的,可以看下前言里面的链接。

1.1 并查集数据结构构造

这里的难点就是:

  • 如何将二维数组转化为一维数组。假设二维数组下标(i,j),长len1,高len2.
  • 那么二维下标转化为一维坐标就是:i*len2 + j
class UnionFind {
    private int[] parent;
    private int[] rank;
    private int sum;

    public UnionFind(char[][] grid) {
        // 初始化岛屿数量为0,因为我们还没有遍历数组,不知道岛屿的数量
        sum = 0;
        int len1 = grid.length;
        int len2 = grid[0].length;
        parent = new int[len1 * len2];
        rank = new int[len1 * len2];
        for (int i = 0; i < len1; i++) {
            for (int j = 0; j < len2; j++) {
                // 根节点指向自己
                parent[i * len2 + j] = i * len2 + j;
                // 如果这个地方是岛屿,那么该元素对应的集合内元素数量为1
                if (grid[i][j] == '1') {
                    rank[i * len2 + j] = 1;
                    // 岛屿数量+1
                    sum++;
                }
            }
        }
    }

    public int find(int x) {
        while (x != parent[x]) {
            x = parent[x];
        }
        return x;
    }

    public void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX == rootY) {
            return;
        }
        // 如果根节点 rootX 的深度 > rootY。
        if (rank[rootX] > rank[rootY]) {
            // 那么将以rootY作为根节点的集合加入到rootX对应的集合当中
            rank[rootX] += rank[rootY];
            // 同时改变rootY的根节点,指向rootX。
            parent[rootY] = rootX;
        } else {
            // 反之
            rank[rootY] += rank[rootX];
            parent[rootX] = rootY;
        }
        // 岛屿数量-1
        sum--;
    }
}

1.2 使用并查集编码

public int numIslands(char[][] grid) {
    int len1 = grid.length;
    int len2 = grid[0].length;
    UnionFind unionFind = new UnionFind(grid);
    for (int i = 0; i < len1; i++) {
        for (int j = 0; j < len2; j++) {
            // 如果当前是岛屿
            if (grid[i][j] == '1') {
                // 先将当前的岛屿标识改变,避免被重复访问
                grid[i][j] = '0';
                // 分别朝4个方向,上下左右访问,如果是岛屿,开始合并
                if (i - 1 >= 0 && grid[i - 1][j] == '1') {
                    unionFind.union(i * len2 + j, (i - 1) * len2 + j);
                }
                if (i + 1 < len1 && grid[i + 1][j] == '1') {
                    unionFind.union(i * len2 + j, (i + 1) * len2 + j);
                }
                if (j - 1 >= 0 && grid[i][j - 1] == '1') {
                    unionFind.union(i * len2 + j, i * len2 + j - 1);
                }
                if (j + 1 < len2 && grid[i][j + 1] == '1') {
                    unionFind.union(i * len2 + j, i * len2 + j + 1);
                }
            }
        }
    }
    // 最后返回岛屿的数量
    return unionFind.sum;
}

最终完整代码如下:

public class Test200 {
    public int numIslands(char[][] grid) {
        int len1 = grid.length;
        int len2 = grid[0].length;
        UnionFind unionFind = new UnionFind(grid);
        for (int i = 0; i < len1; i++) {
            for (int j = 0; j < len2; j++) {
                // 如果当前是岛屿
                if (grid[i][j] == '1') {
                    // 先将当前的岛屿标识改变,避免被重复访问
                    grid[i][j] = '0';
                    // 分别朝4个方向,上下左右访问,如果是岛屿,开始合并
                    if (i - 1 >= 0 && grid[i - 1][j] == '1') {
                        unionFind.union(i * len2 + j, (i - 1) * len2 + j);
                    }
                    if (i + 1 < len1 && grid[i + 1][j] == '1') {
                        unionFind.union(i * len2 + j, (i + 1) * len2 + j);
                    }
                    if (j - 1 >= 0 && grid[i][j - 1] == '1') {
                        unionFind.union(i * len2 + j, i * len2 + j - 1);
                    }
                    if (j + 1 < len2 && grid[i][j + 1] == '1') {
                        unionFind.union(i * len2 + j, i * len2 + j + 1);
                    }
                }
            }
        }
        // 最后返回岛屿的数量
        return unionFind.sum;
    }

    class UnionFind {
        private int[] parent;
        private int[] rank;
        private int sum;

        public UnionFind(char[][] grid) {
            // 初始化岛屿数量为0,因为我们还没有遍历数组,不知道岛屿的数量
            sum = 0;
            int len1 = grid.length;
            int len2 = grid[0].length;
            parent = new int[len1 * len2];
            rank = new int[len1 * len2];
            for (int i = 0; i < len1; i++) {
                for (int j = 0; j < len2; j++) {
                    // 根节点指向自己
                    parent[i * len2 + j] = i * len2 + j;
                    // 如果这个地方是岛屿,那么该元素对应的集合内元素数量为1
                    if (grid[i][j] == '1') {
                        rank[i * len2 + j] = 1;
                        // 岛屿数量+1
                        sum++;
                    }
                }
            }
        }

        public int find(int x) {
            while (x != parent[x]) {
                x = parent[x];
            }
            return x;
        }

        public void union(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);
            if (rootX == rootY) {
                return;
            }
            // 如果根节点 rootX 的深度 > rootY。
            if (rank[rootX] > rank[rootY]) {
                // 那么将以rootY作为根节点的集合加入到rootX对应的集合当中
                rank[rootX] += rank[rootY];
                // 同时改变rootY的根节点,指向rootX。
                parent[rootY] = rootX;
            } else {
                // 反之
                rank[rootY] += rank[rootX];
                parent[rootX] = rootY;
            }
            // 岛屿数量-1
            sum--;
        }
    }
}

二. 岛屿的最大面积

原题链接

在这里插入图片描述
在这里插入图片描述

这个题目就是在第一题的基础上,查找最大的集合深度,即rank的最大值。我们只需要在第一题的基础上,增加一个循环判断即可:

int maxArea = 0;
for (int i = 0; i < len1 * len2; i++) {
    maxArea = Math.max(maxArea, unionFind.rank[i]);
}
return maxArea;

注意:

  • 本题是int类型的数组,你可以全局替换一下字符:将 ' 替换成 char 替换成int

最终完整代码如下:

public class Test695 {
    public int maxAreaOfIsland(int[][] grid) {
        int len1 = grid.length;
        int len2 = grid[0].length;
        UnionFind unionFind = new UnionFind(grid);
        for (int i = 0; i < len1; i++) {
            for (int j = 0; j < len2; j++) {
                // 如果当前是岛屿
                if (grid[i][j] == 1) {
                    // 先将当前的岛屿标识改变,避免被重复访问
                    grid[i][j] = 0;
                    // 分别朝4个方向,上下左右访问,如果是岛屿,开始合并
                    if (i - 1 >= 0 && grid[i - 1][j] == 1) {
                        unionFind.union(i * len2 + j, (i - 1) * len2 + j);
                    }
                    if (i + 1 < len1 && grid[i + 1][j] == 1) {
                        unionFind.union(i * len2 + j, (i + 1) * len2 + j);
                    }
                    if (j - 1 >= 0 && grid[i][j - 1] == 1) {
                        unionFind.union(i * len2 + j, i * len2 + j - 1);
                    }
                    if (j + 1 < len2 && grid[i][j + 1] == 1) {
                        unionFind.union(i * len2 + j, i * len2 + j + 1);
                    }
                }
            }
        }
        int maxArea = 0;
        for (int i = 0; i < len1 * len2; i++) {
            maxArea = Math.max(maxArea, unionFind.rank[i]);
        }
        return maxArea;
    }

    class UnionFind {
        private int[] parent;
        private int[] rank;
        private int sum;

        public UnionFind(int[][] grid) {
            // 初始化岛屿数量为0,因为我们还没有遍历数组,不知道岛屿的数量
            sum = 0;
            int len1 = grid.length;
            int len2 = grid[0].length;
            parent = new int[len1 * len2];
            rank = new int[len1 * len2];
            for (int i = 0; i < len1; i++) {
                for (int j = 0; j < len2; j++) {
                    // 根节点指向自己
                    parent[i * len2 + j] = i * len2 + j;
                    // 如果这个地方是岛屿,那么该元素对应的集合内元素数量为1
                    if (grid[i][j] == 1) {
                        rank[i * len2 + j] = 1;
                        // 岛屿数量+1
                        sum++;
                    }
                }
            }
        }

        public int find(int x) {
            while (x != parent[x]) {
                x = parent[x];
            }
            return x;
        }

        public void union(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);
            if (rootX == rootY) {
                return;
            }
            // 如果根节点 rootX 的深度 > rootY。
            if (rank[rootX] > rank[rootY]) {
                // 那么将以rootY作为根节点的集合加入到rootX对应的集合当中
                rank[rootX] += rank[rootY];
                // 同时改变rootY的根节点,指向rootX。
                parent[rootY] = rootX;
            } else {
                // 反之
                rank[rootY] += rank[rootX];
                parent[rootX] = rootY;
            }
            // 岛屿数量-1
            sum--;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zong_0915

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值