想要精通算法和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--;
        }
    }
}
相关推荐
Python私教19 分钟前
model中能定义字段声明不存储到数据库吗
数据库·oracle
LNTON羚通2 小时前
摄像机视频分析软件下载LiteAIServer视频智能分析平台玩手机打电话检测算法技术的实现
算法·目标检测·音视频·监控·视频监控
BestandW1shEs3 小时前
谈谈Mysql的常见基础问题
数据库·mysql
重生之Java开发工程师3 小时前
MySQL中的CAST类型转换函数
数据库·sql·mysql
教练、我想打篮球3 小时前
66 mysql 的 表自增长锁
数据库·mysql
Ljw...3 小时前
表的操作(MySQL)
数据库·mysql·表的操作
哥谭居民00013 小时前
MySQL的权限管理机制--授权表
数据库
哭泣的眼泪4083 小时前
解析粗糙度仪在工业制造及材料科学和建筑工程领域的重要性
python·算法·django·virtualenv·pygame
wqq_9922502773 小时前
ssm旅游推荐系统的设计与开发
数据库·旅游