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

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

  • 前言
  • [一. 岛屿数量](#一. 岛屿数量)
    • [1.1 并查集数据结构构造](#1.1 并查集数据结构构造)
    • [1.2 使用并查集编码](#1.2 使用并查集编码)
  • [二. 岛屿的最大面积](#二. 岛屿的最大面积)

前言

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

一. 岛屿数量

原题链接

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

1.1 并查集数据结构构造

这里的难点就是:

  • 如何将二维数组转化为一维数组。假设二维数组下标(i,j),长len1,高len2.
  • 那么二维下标转化为一维坐标就是:i*len2 + j
java 复制代码
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 使用并查集编码

java 复制代码
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;
}

最终完整代码如下:

java 复制代码
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的最大值。我们只需要在第一题的基础上,增加一个循环判断即可:

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

注意:

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

最终完整代码如下:

java 复制代码
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--;
        }
    }
}
相关推荐
88号技师1 小时前
2024年12月一区SCI-加权平均优化算法Weighted average algorithm-附Matlab免费代码
人工智能·算法·matlab·优化算法
IT猿手1 小时前
多目标应用(一):多目标麋鹿优化算法(MOEHO)求解10个工程应用,提供完整MATLAB代码
开发语言·人工智能·算法·机器学习·matlab
88号技师1 小时前
几款性能优秀的差分进化算法DE(SaDE、JADE,SHADE,LSHADE、LSHADE_SPACMA、LSHADE_EpSin)-附Matlab免费代码
开发语言·人工智能·算法·matlab·优化算法
问道飞鱼2 小时前
【知识科普】认识正则表达式
数据库·mysql·正则表达式
HaiFan.2 小时前
SpringBoot 事务
java·数据库·spring boot·sql·mysql
水根LP492 小时前
linux系统上SQLPLUS的重“大”发现
数据库·oracle
我要学编程(ಥ_ಥ)2 小时前
一文详解“二叉树中的深搜“在算法中的应用
java·数据结构·算法·leetcode·深度优先
埃菲尔铁塔_CV算法2 小时前
FTT变换Matlab代码解释及应用场景
算法
途途途途3 小时前
精选9个自动化任务的Python脚本精选
数据库·python·自动化
许野平3 小时前
Rust: enum 和 i32 的区别和互换
python·算法·rust·enum·i32