目录
一. 逻辑结构—“集合”
在集合下,两个元素之间的关系只有两种,即从属于同一个子集或从属于不同子集。
如下图:A与B输入同一个子集,A与C属于不同子集
可以联想到在森林中,森林是m(m≥0)棵互不相交的树的集合
进而联想,同一子集中的各个元素,组织成一棵树来表示(不同集合放到树里)
二. 存储结构
双亲表示法最适合并查集
原因其中并查集的两个操作:
查,一路向上找到其唯一对应的根节点(有几个根节点,就有几棵树,就有几个集合)。
并,其中一个树的根结点指向另一个树的根结点
双亲表示法实现就很方便
图中L 中指向数组下标4,也就是E
E中指向数组下标为 1,也就是B
B指向数组下标为0,也就是A
A指向数组下标-1,为根结点
初始化并查集,将各个元素初始化为各自独立的子集。
#define SIZE 13
int UFSets[SIZE]; //集合元素数组
//初始化并查集
void Initial(int s[]){
for(int i=0;i<SIZE;i++)
s[i]=-1;
}
三. 集合相关操作
3.1 查
Find ——“查”操作:确定一个指定元素所属集合
// Find 操作,找 x 所属集合(返回 x 所属根结点)
int Find(int s[], int x) {
while (s[x] >= 0)
x = s[x]; // 循环寻找 x 的根
return x; // 返回根节点的下标
}
//查11号元素L,指向4
// 4号元素指向1,
// 1号元素指向0
// 0号元素指向-1,退出while return 0;
最坏时间复杂度:O(n) ,与树的高度有关
Find操作的优化(压缩路径)
之前使用的Find操作是从指定节点出发,根据s[ ],向上找到所属根节点,这样向上的路径称为“查找路径”,而Find的优化操作就是要压缩这条路径,即“压缩路径”。具体操作就是先找到根节点,再将查找路径上所有结点都挂到根结点下。
节点L的查找路径:
压缩路径就是将图中E,B全部挂到A节点下。这样,从L节点向上找根节点的路径就被压缩了。
代码如下
//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操作时间开销都很低。
具体地,Find最坏时间复杂度为,将n个独立元素通过多次Union合并为一个集合的最坏时间复杂度为(n个元素需要合并n-1次)。
3.2 并
Union ——“并”操作:将两个不想交的集合合并为一个
//Union"并"操作,将两个集合合并为一个
//Root1,Root2为两个集合的根结点,如上图C指向A
void Union(int s[],int Root1,int Root2){
//要求Root1与Root2是不同的集合
if(Rootl==Root2) return;
//将根Root2连接到另一根Root1下面
S[Root2]=Root1;
}
//时间复杂度:O(1)
Union操作的优化(小树合并到大树)
为了使“查”的效率更高,合并树时可以让小树合并到大树中,这样就不会增加树的高度了。
可以用根节点的绝对值表示树的结点总数。如下图
//Union"并"操作,小树合并到大树
void Union(int S[],int Rootl,int Root2){
if(Rootl == Root2) return;
if(S[Root2]>S[Root1]){ //Root2结点数更少 ,谁大谁结点少
S[Root1]+= S[Root2]; //累加结点总数
S[Root2]=Rootl; //小树合并到大树
} else {
S[Root2]+= S[Root1]; //累加结点总数
S[Root1]=Root2; //小树合并到大树
}
}
//改进的Union操作时间复杂度依旧是O(1)
用该方法优化“Union”操作后,构造的树高不超过,那么Find操作的最坏时间复杂度也能到O(
),将n个独立元素通过多次Union合并为一个集合的最坏时间复杂度为O(n*log2^n)
3.3 总结
用数组、双亲表示法来描述元素之间的集合关系。根节点为 -1,非根节点指向父节点下标。
Find (int S[], x) ——找到x所属集合Union(int S[], int x, int y) —— 将x、y所属集合合并
最坏时间复杂度:Find 操作 = 最坏树高 = O(n)
将n个独立元素通过多次Union合并为一个集合——
Union优化后:
用根节点的负值表示一棵树的结点总数,每次 Union 操作让小树合并到大树根节点下面。
最坏时间复杂度:Find 操作=最坏树高=
将n个独立元素通过多次Union合并为一个集合——
在进一步 Find优化:
“压缩路径”的策略,每次 Find 操作先找到 x 所属根节点,再将查找路径上的所有结点都直接挂在根节点下面。
最坏时间复杂度:Find 操作=最坏树高= 𝑂(α(𝑛))
将n个独立元素通过多次Union合并为一个集合——O(n α(𝑛))