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

题目描述

给定一个由不同正整数组成的非空数组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]++;
            }
        }
    }
}

 

代码实现


import java.util.HashMap;
import java.util.Map;

class Solution {
    public int largestComponentSize(int[] nums) {
        UnionFind uf = new UnionFind(maxNum(nums) + 1);

        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;
                    if (v1 == v2) {
                        uf.merge(num, v1);
                    } else {
                        uf.merge(num, v1);
                        uf.merge(num, v2);
                    }
                }
            }
        }

        Map<Integer, Integer> countMap = new HashMap<>();
        int max = 1;
        for (int num : nums) {
            int root = uf.find(num);
            int count = countMap.getOrDefault(root, 0);
            count++;
            max = Math.max(max, count);
            countMap.put(root, count);
        }
        return max;
    }

    int maxNum(int[] nums) {
        int max = nums[0];
        for (int i = 1; i < nums.length; i++) {
            max = Math.max(max, nums[i]);
        }
        return max;
    }

    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]++;
                }
            }
        }
    }

    public static void main(String[] args) {
        Solution solution = new Solution();

        System.out.println(solution.largestComponentSize(new int[]{
                2, 3, 6, 7, 4, 12, 21, 39
        }));
    }
}

总结

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值