并查集
1.概念
简而言之,合并集合与查找集合于一身的数据算法。
2.性质
并查集算法不支持分隔一个集合。
3. 算法
一个集合,使用某个元素x作为代表元,来代表这个元素集合,所有元素组成以代表元为根的树形结构,对每个元素y的父节点都可指向根节点,即parent[y] = x。
所以,判断两个元素是否属于统一集合,只需要看他们代表元是否相等即可。
4. 并查集操作
一个并查集有三个操作。
- 集合初始化
建立一个新的并查集,里面包括多个单个元素的单独集合,每个单集合包含以下三个东西。
- 集合元素
- 集合的层次,通常用rank表示
- 该集合类别
结构表示方法:
struct Node
{
int data;
int rank;
int parent;
}
数组表示方法:
int set[max];//集合index的类别,或者用parent表示
int rank[max];//集合index的层次,通常初始化为0
int data[max];//集合index的数据类型
//初始化集合
void Make_Set(int i)
{
set[i]=i;//初始化的时候,一个集合的parent都是这个集合自己的标号。没有跟它同类的集合,那么这个集合的源头只能是自己了。
rank[i]=0;
}
- 查找函数
即查找集合parent的方法,如果集合的parent等于集合的编号(即还没有被合并或者没有同类),那么自然返回自身编号。
如果不同(即经过合并操作后指针指向了源头(合并后选出的rank高的集合))那么就可以调用递归函数。
int get_parent(int x)
{
if(node[x].parent==x)
return x;
return get_parent(node[x].parent);
}
但是,如果每次都沿着父节点不断向上查找,时间复杂度就是树的高度,为了更高效,引入路径压缩的概念。
即每次查找时,令查找路径上的每个节点都直接指向根节点。
- 合并集合函数
void Union(int a,int b)
{
a=get_parent(a);
b=get_parent(b);
if(node[a].rank>node[b].rank)
node[b].parent=a;
else
{
node[a].parent=b;
if(node[a].rank==node[b].rank)
node[b].rank++;
}
}
简而言之,就是将一个集合的树根指向另一个集合的根,
两种合并策略:
- 按秩合并
秩是指树高度的上界,即将较矮的树添加到较高的树根节点上。
- 按元素个数合并
节点少的树合并到节点多的树根节点。
参考:
https://segmentfault.com/a/1190000004023326
https://www.jianshu.com/p/8c74df1db116