1.并查集
- 有若干个样本a、b、c、d…类型假设是V
- 在并查集中一开始认为每个样本都在单独的集合里
- 用户可以在任何时候调用如下两个方法 :
boolean isSameSet(V x, V y) : 查询样本x和样本y是否属于一个集合
void union(V x, V y) : 把x和y各自所在集合的所有样本合并成一个集合 - isSameSet和union方法的代价越低越好
- 并查集有两个基本操作:
Find: 查找元素所属子集
Union:合并两个子集为一个新的集合
2.C++实现(未优化版本)
#include <vector>
using namespace std;
class UnionFind
{
private:
vector<int> parent;
int sets;
public:
UnionFind(int max_size) : parent(vector<int>(max_size))
{
// 初始化每一个元素的父亲节点都为自身
for(int i = 0;i < max_size;i++)
{
parent[i] = i;
}
}
int find(int x)
{
return parent[x] == x ? x : find(parent[x]);
}
void union(int i, int j)
{
parent[find(i)] = find(j);
sets--;//更新所含集合数目
}
// 判断两个元素是否属于同一个集合
bool isSameSet(int i, int j)
{
return find(i) == find(j);
}
int sets() {
return sets;
}
};
3.C++实现(优化版本)
采用“路径压缩”与“按秩合并”优化的并查集
- “按秩合并”。实际上就是在合并两棵树时,将高度较小的树合并到高度较大的树上。
- “路径压缩”。在执行find的过程中,将路径上的所有节点都直接连接到根节点上,以使得合并后的树的链长度最短。
class UnionFind{
public:
// parent[i] = k : i的父亲是k
vector<int> parent;
// rank[i] = k : 如果i是代表节点,rank[i]才有意义,否则无意义
// i所在的集合大小是多少
vector<int> rank;
// 一共有多少个集合
int sets;
UnionFind(int size) : parent(vector<int> (size)), rank(vector<int> (size, 1)), set(size){
for (int i = 0; i < size; ++i){
parent[i] = i;
}
int findparent(int x){
return x == parent[x] ? x : (parent[x] = findparent(parent[x]));
}
void to_union(int a, int b){
int pa = findparent(a);
int pb = findparent(b);
if (pa != pb){
if (rank[pa] >= rank[pb]){//注意使用父节点的rank进行比较
parent[pb] = pa;
rank[pa] += rank[pb];
}
else{
parent[pa] = pb;
rank[pb] += rank[pa];
}
set--;
}
}
bool isSameSet(int a, int b){
return findparent(a) == findparent(b);
}
};