携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
题目描述
给定一个由不同正整数组成的非空数组,初始时可以把数组中的各个元素看为一个图中的节点,各节点互不相连,要求根据数组中的元素,若nums[i] 与 nums[j] 有大于 1 的公约数,则认为该两点间存在一条边,处理数组的的所有元素,返回图中最大连通组件的大小;
例1:输入:"[15,89,6,25,34,59,23,16]" 输出:"5"
解释:15 与 6 有大于 1 的公约数 3 , 15 和 25 有大于 1 的公约数 5.......,处理完全部元素后,得到的图如下图,所以最大连通组件的大小为5.
本题值得注意的地方
- 所给的数组中的元素是各不相同的
- 数组的长度范围 1 ~ 2 * 1e4
- 数组中的数据范围 1 ~ 1e5
原题地址:952. 按公因数计算最大组件大小
解题思路
看到这种分组的问题时,首先想到的就是利用并查集来解决,但是该题怎样分组是个问题。若是对数组中的每对数都进行判断是否有公约数之后再进行分组处理,那么根据该题所给出的数据量来看该方法的时间复杂度必定是超时的,所以一对一对的比较是行不通的。可以看到该题给出的数据范围最大就是 1e5 ,我们可以根据每个数的因数进行分组。
在对每个数进行计算因数时,只需要判断到 即可,将该数与其所有的因数在并查集中合并。如例 1 的输入数据,先对15进行处理后,合并 15与3 所在的集合 以及15与5 所在的集合、合并 6与2 所在的集合 以及 6与3 所在的集合、合并25与5所在的集合······,如下图所示,这里仅仅给出了合并15、6、25、34、16所在的集合。
集合处理完毕后,可以利用 map 计算各集合中所包含点的个数,最后集合中包含最多的点数即为最大连通组件的大小。
实现代码
class Solution {
public:
// 记录各点所属的集合
int t[100010];
// 寻找某一个点所属的集合
int find(int x){
if(t[x]!=x) t[x] = find(t[x]);
return t[x];
}
// 合并两个点所在的集合
void merge(int x,int y){
if(find(x) != find(y)){
t[find(x)] = find(y);
}
}
int largestComponentSize(vector<int>& nums) {
// 用于对各集合包含的点进行计数
unordered_map<int,int> map;
// 初始化集合
for(int i = 0;i < 100010;i++) t[i] = i;
// 处理各点
for(int num:nums){
for(int j = 2;j*j<=num;j++){
if(num%j == 0){
merge(num,j);
merge(num,num/j);
}
}
}
int res = 0;
for(auto num:nums) map[find(num)]++;
// 找到集合中包含最多的点数
for(auto t=map.begin();t != map.end();t++) res = max(res,t->second);
return res;
}
};