在集合处理中,在初始状态,每个元素构成一个单元素的集合,然后按照一定的顺序将属于同一组的元素合并在同一个集合中,需要反复查找一个元素在哪个集合中,这类问题看似不复杂,但数据量极大,使用正常的数据结构来描述,在空间上过大,计算机无法承受,即使空间通过,时间复杂度也极高,在算法设计中很难在短时间内通过。
因此,就需要用并查集,是一种树形的数据结构。
主要用于解决一些元素分组的问题,管理一系列不相交的集合。
并查集主要有三个操作:
把每个点所在的集合,初始化为其自身组成的一个单元素的集合。
通常来说,这个步骤在每次使用该数据结构时只需要执行一次,时间复杂度O(n)。
查找元素所在的集合,即根节点
将两个元素所在的集合合并为一个集合。
通常来说,合并之前,应先判断两个元素是否属于同一集合,这可用上面的“查找”操作实现。
class UnionFind {
private:
unordered_map<int,int> father;
public:
void init(vector<int> nums) {
for(int i = 0; i<nums.size(); i++) {
father.emplace(nums[i],nums[i]);
}
}
int find(int x) {
if(father.count(x) == 0) return NULL;
if(father[x] == x) return x; //不需要做后续处理,自己就是根
int root = x;
while(root != father[root]) root = father[root];
while(x != father[x]) {
int cur = x;
x = father[x];
// father.emplace(cur,root); //emplace 插入时,key存在则不变,就是这个地方导致超时
father[cur] = root;
}
return root;
}
void merge_union(int x, int y) {
//y合并到x内,即x的根节点作为合并后的总根
x = find(x);
y = find(y);
if(x == y) return;
if(x==INT_MAX || y==INT_MAX) return;
father[y] = x;
}
void print_ufset() {
for(unordered_map<int,int>::iterator it=father.begin(); it!=father.end(); it++)
cout<<"num: "<<it->first<<" father: "<< it->second<<endl;
}
};
后续补充......
原文:https://www.cnblogs.com/kyrieliu/p/unionfind.html