• 【图】按公因数计算最大组件大小 并查集


    题目描述

    给定一个由不同正整数组成的非空数组nums,求每个数的公因数,如果nums[i]和nums[j]存在公因数,则说明这2个数有一条边;

    请返回最大的连通性大小,最大的连通性就是能有一条边连接到一起的节点总数。

    示例1:

     

    输入:nums = [4, 6, 15, 25, 11, 77]

    输出:4

    解释:[4, 6, 15, 25]的连通性是4,[11, 77]的连通性是2

    解题思路

    这道题实际考察的是图的连通性问题;

    可以拆解成3个题目,第一个求公因数,第二个使用并查集求最大连通性,第三个对并查集的深度做优化。

    求公因数

    从2到nums[i]的平方根,逐个计算,计算这个数的公因数。 代码如下所示:

            for (int num : nums) {
                int sqrt = (int) Math.sqrt(num);
                for (int i = 2; i <= sqrt; i++) {
                    if (num % i == 0) {
                        int v1 = i;
                        int v2 = num / i;
                    }
                }
            }

    并查集

    通过给定节点找到这个数到根节点

    1. 初始化,把每个点所在集合初始化为其自身。通常来说,这个步骤在每次使用该数据结构时只需要执行一次,无论何种实现方式,时间复杂度均为O(N)。
    2. 查找,查找元素所在的集合,即根节点。
    3. 合并,将两个元素所在的集合合并为一个集合。通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。

     代码如下:

    static class UnionFind {
    
        Integer[] parent;
    
        public UnionFind(int n) {
            parent = new Integer[n];
        }
    
        public Integer find(int v) {
            int temp = v;
            while (parent[temp] != null) {
                temp = parent[temp];
            }
            return temp;
        }
    
        public void merge(int v0, int v1) {
            int root0 = find(v0);
            int root1 = find(v1);
            if (root0 > root1) {
                parent[root1] = root0;
            } else if (root0 < root1) {
                parent[root0] = root1;
            }
        }
    }
    

    并查集优化

    上面并查集代码的实现其实没有考虑查询的深度,这个深度会导致查询耗时过高的问题,具体描述如下图所示:

     

    从深度来分析,一定要采用方案二才能减小遍历的次数。具体实现时需要如下考虑:

    1. 如果层数一致,则取其中一个节点作为根节点,并对根节点的层数加一。
    2. 否则选取层数更大的节点,将这个节点作为根节点继续处理。

    代码实现如下:

    static class UnionFind {
    
        Integer[] parent;
        int[] rank;
    
        public UnionFind(int n) {
            parent = new Integer[n];
            rank = new int[n];
        }
    
        public Integer find(int v) {
            int temp = v;
            while (parent[temp] != null) {
                temp = parent[temp];
            }
            return temp;
        }
    
        public void merge(int v0, int v1) {
            int root0 = find(v0);
            int root1 = find(v1);
    
            if (root0 != root1) {
                if (rank[root0] > rank[root1]) {
                    parent[root1] = root0;
                } else if (rank[root1] > rank[root0]) {
                    parent[root0] = root1;
                } else {
                    parent[root0] = root1;
                    rank[root1]++;
                }
            }
        }
    }

     

    代码实现

    1. import java.util.HashMap;
    2. import java.util.Map;
    3. class Solution {
    4. public int largestComponentSize(int[] nums) {
    5. UnionFind uf = new UnionFind(maxNum(nums) + 1);
    6. for (int num : nums) {
    7. int sqrt = (int) Math.sqrt(num);
    8. for (int i = 2; i <= sqrt; i++) {
    9. if (num % i == 0) {
    10. int v1 = i;
    11. int v2 = num / i;
    12. if (v1 == v2) {
    13. uf.merge(num, v1);
    14. } else {
    15. uf.merge(num, v1);
    16. uf.merge(num, v2);
    17. }
    18. }
    19. }
    20. }
    21. Map countMap = new HashMap<>();
    22. int max = 1;
    23. for (int num : nums) {
    24. int root = uf.find(num);
    25. int count = countMap.getOrDefault(root, 0);
    26. count++;
    27. max = Math.max(max, count);
    28. countMap.put(root, count);
    29. }
    30. return max;
    31. }
    32. int maxNum(int[] nums) {
    33. int max = nums[0];
    34. for (int i = 1; i < nums.length; i++) {
    35. max = Math.max(max, nums[i]);
    36. }
    37. return max;
    38. }
    39. static class UnionFind {
    40. Integer[] parent;
    41. int[] rank;
    42. public UnionFind(int n) {
    43. parent = new Integer[n];
    44. rank = new int[n];
    45. }
    46. public Integer find(int v) {
    47. int temp = v;
    48. while (parent[temp] != null) {
    49. temp = parent[temp];
    50. }
    51. return temp;
    52. }
    53. public void merge(int v0, int v1) {
    54. int root0 = find(v0);
    55. int root1 = find(v1);
    56. if (root0 != root1) {
    57. if (rank[root0] > rank[root1]) {
    58. parent[root1] = root0;
    59. } else if (rank[root1] > rank[root0]) {
    60. parent[root0] = root1;
    61. } else {
    62. parent[root0] = root1;
    63. rank[root1]++;
    64. }
    65. }
    66. }
    67. }
    68. public static void main(String[] args) {
    69. Solution solution = new Solution();
    70. System.out.println(solution.largestComponentSize(new int[]{
    71. 2, 3, 6, 7, 4, 12, 21, 39
    72. }));
    73. }
    74. }

    总结

    这道题挺好的,既要解决公因数,又要做并查集,最后还要做优化,才能完整解决这个问题。第一次做只能完成解决公因数和并查集,对并查集的优化也是看其他的代码分析才最终解决的。

  • 相关阅读:
    谷粒商城15-商品秒杀、Sentinel高并发、高并发方法论
    微信小程序实现预约生成二维码
    Vue2 零基础入门 Vue2 零基础入门第三天 3.4 vue组件
    布隆过滤器及其用法
    最新整理源码面试题
    nodeJs--各种路径
    还在为数据库事务一致性检测而苦恼?让Elle帮帮你,以TDSQL为例我们测测 | DB·洞见#7
    零基础学习React(Html)
    OpenAI的组织形态、决策机制与产品构建
    C语言实现通讯录
  • 原文地址:https://blog.csdn.net/weiliuhong1/article/details/126075377