给定一个由不同正整数组成的非空数组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;
}
}
}
通过给定节点找到这个数到根节点
代码如下:
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; } } }
上面并查集代码的实现其实没有考虑查询的深度,这个深度会导致查询耗时过高的问题,具体描述如下图所示:
从深度来分析,一定要采用方案二才能减小遍历的次数。具体实现时需要如下考虑:
代码实现如下:
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
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
- }));
- }
- }
这道题挺好的,既要解决公因数,又要做并查集,最后还要做优化,才能完整解决这个问题。第一次做只能完成解决公因数和并查集,对并查集的优化也是看其他的代码分析才最终解决的。