实现原理
一种树型数据结构,用于处理不相交集合(Disjoint Sets)的合并以及查询;一开始让所有元素独立成树,也就是只有根节点的树;然后根据需要将关联的元素(树)进行合并;合并的方式仅仅是将一棵树最原始的节点的父亲索引指向另一棵树;
初始化
初始化father:各个节点独立成树,并且其father[i]=-1,
father[i]
0 1 2 3 4 5 6 7 8 9
-1 -1 -1 -1 -1-1-1-1 -1
每个集合里面存放的都是自己的父节点,一直找,直到一个节点的值为负数,表示该节点为这个集合的根节点。根节点的绝对值,表示该集合有多少个元素
FindIdx查找该节点的根
因为每个节点的值为这个节点的父节点,所以以节点的值为下标,一直向上查找,直到一个节点的值为负数,那么找到了根节点。即:该集合的代表。
合并Union
找到两个节点的根节点,将他们的值相加存放到根节点1,根节点2的值为节点1的下标。这样将两个集合合并成以根节点1的集合中。
代码实现
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
using namespace std;
// 0 1 2 3 4 5 6
// -1 -1 -1 -1 -1 -1 -1
class UnionFindSet
{
public:
UnionFindSet(size_t size)
{
s.resize(size, -1);
}
size_t FindIdx(int x)//找该位置的根节点的下标
{
int root = x;
while (s[root] >= 0)
root = s[root];
return root;
}
void Union(int x1, int x2)//合并两个
{
int root1 = FindIdx(x1);
int root2 = FindIdx(x2);
if (root1 != root2)
{
s[root1] += s[root2];
s[root2] = root1;
}
}
bool IsSameSet(int x1, int x2)//判断两个集合是否相等
{
return FindIdx(x1) == FindIdx(x2);
}
size_t UnionSize()//求有几个集合 0 不是一个集合
{
size_t count = 0;
for (size_t i = 0; i < s.size(); ++i)
{
if (s[i] < 0)
count++;
}
return count - 1;
}
void PrintSet()
{
for (size_t i = 0; i < s.size(); ++i)
cout << i << " ";
cout << endl;
for (size_t i = 0; i < s.size(); ++i)
{
if (s[i] < 0)
cout << s[i] << " ";
else
cout << s[i] << " ";
}
cout << endl;
}
private:
vector<int> s;
};
void Test()
{
UnionFindSet un(8);
un.Union(1, 3); // -1
un.Union(3, 7);
un.Union(2, 4);
un.Union(4, 5);
un.Union(5, 6);
un.PrintSet();
cout<< un.UnionSize()<<endl;
}
初始化
// 0 1 2 3 4 5 6 7 8
// -1 -1 -1 -1 -1 -1 -1 -1 -1
(1)un.Union(1, 3);
查找1的根节点为 本身,查找 3的根节点为本身,然后合并,
1的值 = -1 + -1 = -2。//改集合有两个元素。
3的值 = 1;//3的父节点为节点1
// 0 1 2 3 4 5 6 7 8
// -1 -2 -1 1 -1 -1 -1 -1 -1
(2)un.Union(3, 7);
3的根节点为1,而1是该集合的代表,
7集合的代表为本身;
合并,
1的值为 = -2 + -1 = -3;//该集合有三个元素
7的值为 = 1。//节点1
应用
优化——路径压缩
思想
每次查找的时候,如果路径较长,则修改信息,以便下次查找的时候速度更快。
实现
第一步,找到根结点。
第二步,修改查找路径上的所有节点,将它们都指向根结点
图的最小生成树克鲁斯卡尔算法