按公因数计算最大组件大小
难度:
标签:
题目描述
给定一个由不同正整数的组成的非空数组 nums
,考虑下面的图:
- 有
nums.length
个节点,按从nums[0]
到nums[nums.length - 1]
标记; - 只有当
nums[i]
和nums[j]
共用一个大于 1 的公因数时,nums[i]
和nums[j]
之间才有一条边。
返回 图中最大连通组件的大小 。
示例 1:
输入:nums = [4,6,15,35] 输出:4
示例 2:
输入:nums = [20,50,9,63] 输出:2
示例 3:
输入:nums = [2,3,6,7,4,12,21,39] 输出:8
提示:
1 <= nums.length <= 2 * 104
1 <= nums[i] <= 105
nums
中所有值都 不同
代码结果
运行时间: 304 ms, 内存: 30.3 MB
/*
题目思路:
1. 我们需要找到图中最大的连通组件的大小,图中的节点由数组nums中的元素表示,
如果两个元素有一个大于1的公因数,那么它们之间有一条边。
2. 为了求解,我们可以使用并查集(Union-Find)来跟踪每个节点所属的连通分量。
3. 对于每个数,我们找到它的所有因数,并将这些因数所在的集合进行合并。
4. 最终,我们通过查找每个集合的大小来确定最大的连通组件的大小。
*/
import java.util.*;
import java.util.stream.IntStream;
import java.util.stream.Stream;
public class LargestComponentSizeStream {
public int largestComponentSize(int[] nums) {
int max = Arrays.stream(nums).max().orElse(0);
UnionFind uf = new UnionFind(max + 1);
Arrays.stream(nums).forEach(num -> {
IntStream.rangeClosed(2, (int) Math.sqrt(num)).filter(factor -> num % factor == 0).forEach(factor -> {
uf.union(num, factor);
uf.union(num, num / factor);
});
});
Map<Integer, Long> count = Arrays.stream(nums).boxed()
.collect(Collectors.groupingBy(uf::find, Collectors.counting()));
return count.values().stream().max(Long::compareTo).orElse(0L).intValue();
}
class UnionFind {
int[] parent;
int[] rank;
public UnionFind(int size) {
parent = new int[size];
rank = new int[size];
IntStream.range(0, size).forEach(i -> parent[i] = i);
}
public int find(int x) {
if (x != parent[x]) {
parent[x] = find(parent[x]);
}
return parent[x];
}
public void union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
if (rank[rootX] > rank[rootY]) {
parent[rootY] = rootX;
} else if (rank[rootX] < rank[rootY]) {
parent[rootX] = rootY;
} else {
parent[rootY] = rootX;
rank[rootX]++;
}
}
}
}
}
解释
方法:
题解采用了素数筛法结合并查集的方法来解决问题。首先,使用埃拉托斯特尼筛法预先计算出所有小于等于100000的素数,并为每个数标记它的素数因子。然后,对于每个输入数字,通过它的素数因子来定义它在图中的节点。如果两个数有共同的素数因子,它们在图中就是相连的。这样,问题转化为了找出具有共同素数因子的最大连通组件。使用并查集来处理节点的连接和组件的合并。最后,遍历输入数组,对每个数的素数因子进行并查集合并操作,最终通过计数并查集根的频率来确定最大的连通组件大小。
时间复杂度:
O(n log log n + m log n)
空间复杂度:
O(n + m)
代码细节讲解
🦆
在算法中,为什么选择使用埃拉托斯特尼筛法而不是其他方法来找出所有素数?
▷🦆
并查集在此题解中的具体作用是什么?如何通过并查集找到最大的连通组件?
▷🦆
题中提到的并查集的`link`操作中,为什么要比较`rank`并根据比较结果选择不同的合并策略?
▷