摘要:本文介绍了通用并查集的树形实现,通过压缩路径和维持数的平衡,可以保证
查找和合并的平均时间复杂度为O(1)!
关键字:并查集,UnionFind,树形
并查集基本知识参见博文《并查集的数组实现》。在并查集的树形实现中,使用树状结构来组织一个
集合,多个集合之间组成森林。
合并两个集合即合并两棵树T1和T2时,只要将两棵树中的一棵作为另外一棵的子树。为了维护数的
平衡,即减少深度较深的节点的个数,可以把节点较少的数作为节点较深的树的子树。具体实现就是把一棵
树的根节点作为另外一棵树的根节点的子树!
查找某个节点所属的集合时,找到该节点所在树的树根即可,同时,可以将查找经过的所有节点的父
节点都设置为根,以方便下一次的查询,这种策略虽然耗费了时间,但是可以使查找的平均时间复杂度
为O(1)!
基于以上思想的并查集树形实现C++源码如下:
/*
*并查集的树形实现
*/
#include <iostream>
using namespace std;
#define N 10000
struct Node
{
int parent; //父节点编号
int count; //集合中元素的个数
};
Node UnionFindSet[N];
void uf_init(int n); //初始化
int uf_find(int x); //查找元素,返回集合根节点编号
void uf_merge(int x, int y); //合并元素所在的集合树
int main(int argc, char* *argv)
{
int n = 20;
uf_init(n);
uf_merge(1, 5);
uf_merge(10, 13);
uf_merge(15, 17);
uf_merge(1, 13);
uf_merge(5, 17);
uf_merge(19, 18);
if(uf_find(1) == uf_find(15))
{
cout<<"success1"<<endl;
}
if(uf_find(13) == uf_find(17))
{
cout<<"success2"<<endl;
}
if(uf_find(19) != uf_find(17))
{
cout<<"success3"<<endl;
}
system("pause");
return 0;
}
void uf_init(int n)
{
//时间复杂度O(N)
for(int i = 0; i < n; ++i)
{
UnionFindSet[i].count = 1;
UnionFindSet[i].parent = i;
}
}
int uf_find(int x) //x >= 0 && x <n
{
//使用路径压缩之后的平均时间复杂度为O(1)
int i = x;
while(i != UnionFindSet[i].parent)
i = UnionFindSet[i].parent;
//压缩路径,将寻找X时经过的所有节点的父节点设为集合的根节点,方便下次查找
int j = x;
while(j != i)
{
int tmp = UnionFindSet[j].parent;
UnionFindSet[j].parent = i;
j = tmp;
}
return i;
}
void uf_merge(int x, int y)
{
//平均时间复杂度为O(1);
//寻找树根(集合表示符)
x = uf_find(x);
y = uf_find(y);
if(y == x) return;
//将小树挂到大的树下面,可以减少较深节点的数目,维持树的平衡,以便高效查询
if(UnionFindSet[x].count > UnionFindSet[y].count)
{
UnionFindSet[y].parent = x;
UnionFindSet[x].count += UnionFindSet[y].count;
}
else
{
UnionFindSet[x].parent = y;
UnionFindSet[y].count += UnionFindSet[x].count;
}
}
//参考资料:http://www.docin.com/p-46207828.html
|