1.路径压缩
如果我们合并的树很深,存放父节点的数组已经嵌套了n层,按照传统的做法,我们从最下面的节点去找n节点就要寻找n次,这种做法效率很低。这时候,我们就可以引入路径压缩的概念,路径压缩就是在递归找到根节点的时候,把当前节点到根节点之间所有节点的父节点都设置为根节点。
举个例子:
经过路径压缩之后,树的形态就变成了下面的样子:
我们可以看到经过路径压缩的节点及其子树到根节点的深度减小了很多,所以在以后的操作中,查找根节点的速度会快很多
路径压缩代码:
int find_set(intx)
{/*带路径压缩的查找*/
if(x != parent[x]) /*如果不是根节点,让父节点等于父节点的父节点*/
parent[x] = find_set(parent[x]);
return parent[x]; //返回父节点的值
}
如果看了注释代码还不是很懂的话,可以用一组数据模拟一下,加深理解,上面那种是采用递归路径压缩的方法查找元素,但是,递归压缩路径可能会造成溢出栈,会发生运行错误。
下面是非递归方式进行的路径压缩代码:
int find(int x)
{
int k, j, r;
r = x;
while(r != parent[r]) //查找跟节点
r = parent[r]; //找到根节点,用r记录下
k = x;
while(k != r) //非递归路径压缩操作
{
j = parent[k]; //用j暂存parent[k]的父节点
parent[k] = r; //parent[x]指向跟节点
k = j; //k移到父节点
}
return r; //返回根节点的值
}
2.按秩合并(秩在这里可以理解为树的深度)
按秩合并是两个树合并在选取根节点的时候,选择秩比较大的作为根节点。如下图,5和7有联系:
左边树的秩比右边树的秩大,所以我们让左边的作为根节点。
按秩合并代码:(rank数组存放的是结点的秩)
/*按秩合并x,y所在的集合*/
void union_set(int x, int y)
{
x = find_set(x);
y = find_set(y);
if(rank[x] > rank[y])/*让rank比较高的作为父结点*/
parent[y] = x;
else
{
parent[x] = y;
if(rank[x] == rank[y])
rank[y]++;
}
}
为什么秩相等的时候才加1,看下图:
连接后:
在初始化数组的时候,也要注意rank数组的初始化!
代码:
void Init()
{
for(int i=0;i<=n;i++)
{
parent[i]=i;
rank[i]=0;
}
}