并查集
树的存储–双亲表示法
#define MAX_TREE_SIZE 100
typedef struct{ //树中最多结点
ElemType data; //树的结点定义
int parent; //数据元素
}PTNode; //双亲位置域
typedef struct { //树的类型定义
PTNode nodes[MAX_TREE_SIZE]; //双亲表示法
int n; //结点数
}PTree;
"并查集"的代码实现–初始化
#define SIZE 13
int UFSeta[SIZE]//集合元素数组
//初始化并查集
void Initial(int S[])
{
for(int i = 0;i<SIZE;i++)
S[i] = -1;
}
"并查集"的代码实现–并、查
int Find(int S[],int x){
while(S[x] >= 0) //循环寻找x的根
x = S[x];
return x; //根的S[]小于0
}
//Union "并"操作,将两个集合合并为一个
void Union(int S[],int Root1,int Root2)
{
//要求Root1与Root2是不同的集合
if(Root1==Root2) return ;
//将Root2连接到另一根Root1下面
S[Root2] = Root1;
}
时间复杂度分析
Find "查"操作最坏时间复杂度:O(n)
Union "并"操作时间复杂度:O(1)
Union操作的优化
优化思路:再每次Union操作构建树的时候,尽可能让树不长高
- 用根节点的绝对值表示树的结点总数
- Union操作,让小树合并到大树
//Union "并"操作,小树合并到大树
void Union(int S[],int Root1,int Root2)
{
if(Root1==Root2) return;
if(S[Root2]>S[Root1]){ //Root2结点数更少
S[Root1] += S[Root2];//累加结点总数
S[Root2] = Root1;//小树合并到大树
}else{
S[Root2] += S[Root1]; //累加结点总数
S[Root1] += Root2; // 小树合并到大树
}
}
第一步:
第二步:
第三步:
Union操作优化后,Find操作最坏时间复杂度:log2(n)
Find操作的优化(压缩路径)
压缩路径–Find操作,先找到根节点,再将查找路径上的所有结点都挂到根节点
//Find"查"操作优化,先找到根结点,再进行“压缩路径”
int Find(int S[],int x)
{
int root = x;
while(S[root] >= 0) root=S[root];//循环找到根
while(x != root){//压缩路径
int t = S[x]; //t指向x的父节点
S[x]=root; //x直接挂到根节点下
x = t;
}
return root; //返回根节点编号
}
每次Find操作,先找根,再“压缩路径”,可使树的高度不超过O(α(n))。α(n)是一个增长很缓慢的函数,对于常见的n值,通常α(n)<=4,因此优化后并查集的Find、Union操作时间开销都很低
对于常见的n值,通常α(n)<=4,因此优化后并查集的Find、Union操作时间开销都很低