对于一个集合S={a1, a2, …, an-1, an},我们还可以对集合S进一步划分: S1,S2,…,Sm-1,Sm,我们希望能够快速确定S中的两两元素是否属于S的同一子集。
举个栗子,S={0,1, 2, 3, 4, 5, 6},如果我们按照一定的规则对集合S进行划分,假设划分后为S1={1, 2, 4}, S2={3, 6},S3={0, 5},任意给定两个元素,我们如何确定它们是否属于同一子集?某些合并子集后,又如何确定两两关系?基于此类问题便出现了并查集这种数据结构。
并查集有两个基本操作:
Find: 查找元素所属子集
Union:合并两个子集为一个新的集合
实现:
首先设置一个哈希表将每一个元素指向自己,自己做自己的父节点
再设置一个哈希表将父节点再指向这个父节点的父
最后设置一个哈希表 祖宗节点 以及有多少个元素
class Element{
public:
Element(int v)
{
value = v;
}
int operator!=(Element b)
{
return this->value != b.value;
}
int operator==(Element b)
{
return this->value == b.value;
}
int operator<(const Element &b) const
{
return this->value < b.value; //自定义类型做map的key必须重载<
}
public:
int value;
};
class Solution {
public:
map<int, Element> elementMap;
map<Element, Element> fatherMap;
map<Element, int> sizeMap;
void insertMap(vector<int> nums){
for(int i = 0; i < nums.size(); i++)
{
Element ele(nums[i]);
elementMap.insert(pair<int, Element>(nums[i], ele));
fatherMap.insert(pair<Element, Element>(ele, ele));
sizeMap.insert(pair<Element,int>(ele,1));
}
}
Element findHead(Element element)
{
while(element.value != fatherMap.find(element)->second.value)
{
element = fatherMap.find(element)->second;
}
return element;
}
int isSameSet(int a, int b)
{
if(elementMap.find(a)!=elementMap.end() && elementMap.find(b) != elementMap.end())
{
return findHead(elementMap.find(a)->second) == findHead(elementMap.find(b)->second);
}
return 0;
}
void Union(int a, int b)
{
if(elementMap.find(a)!=elementMap.end() && elementMap.find(b) != elementMap.end()){
Element aF = findHead(elementMap.find(a)->second);
Element bF = findHead(elementMap.find(b)->second);
if(aF != bF)
{
Element big = sizeMap.find(aF)->second > sizeMap.find(bF)->second ? aF : bF;
Element small = (big == aF) ? bF : aF;
fatherMap.erase(small);//map重复key不能直接覆盖 会被无视掉 需要先移除再添加
fatherMap.insert(pair<Element,Element>(small, big));
int tempbigNum = sizeMap.find(big)->second;
sizeMap.erase(big);
sizeMap.insert(pair<Element,int>(big, tempbigNum + sizeMap.find(small)->second));
sizeMap.erase(small);
}
}
}
};
优化
Element findHead(Element element)
{
stack<Element> path;
while(element.value != fatherMap.find(element)->second.value)
{
path.push(element);
element = fatherMap.find(element)->second;
}
while(!path.empty())
{
fatherMap.insert(pair<Element,Element>(path.top(), element)); // 将每一个节点都直接连接到祖宗节点
path.pop();
}
return element;
}