并查集 启发式合并详解 + C代码实现

并查集 启发式合并

并查集,这是个比较简单的东西,用森林模拟集合,集合合并则是两颗树的合并,查找集合则是寻找某棵树的根节点

因为不可避免的会遇到各种合并操作,,所以可能导致某棵树的深度较大,,对应的则出现了并查集的路径压缩还有启发式合并

这里不将路径压缩,主要是这个东西比较普及了,而且理解起来不难。

所以直接进入正题:启发式合并

启发式合并

第一次看到这个名称,实在刘汝佳的书上,当时还惊讶,并查集写了一年了,还真不知道这个启发式合并,接着看,发现实现是使用了一个rank数组,,,原书这么说的:

一个优化是:把小的树合并到大树中,这样会让深度不太大。这个优化称为启发式合并。rank[i] 表示 i 的秩,并用秩来代替深度做刚才提到的启发式合并。

然后就懵了,秩这个东西怎么理解……

于是上网寻找,发现很多blog所描述的都和书上几乎一模一样。。

所以自己想办法理解,,还好理解了

数组rank的意义

这里举个例子

假设我们把这些树看成——一个个城市,而这些城市,因发展的程度被分为不同等级,比如小型城市,中型城市,大型城市,城市与城市之间可以合并,并且规定,小型城市和小型城市合并,其中一个城市会升级为中型城市,两个中型城市合并,其中一个城市会升级为高级城市

我们再次做一个更严密的规则:

  1. 只有两个同级城市合并,其中一个城市才能升级
  2. 小型城市合并到大型城市中,大型城市不升级
  3. 等级高的城市必须由等级低的城市组成

回到树——这个问题

我们假设rank[a]表示树a的等级,姑且这么称呼吧,叫做名称也可以。根据上面的规则(转换一下),如果一棵树的等级较高,则这棵树的”规模“会比较高,也就是深度会深一些,根据之前的合并优化规则:

一个优化是:把小的树合并到大树中,这样会让深度不太大。

也就是把等级较低的树合并到等级较高的树上

总结

我们写一个合并函数,这个函数需要根据两棵树的等级(rank)来判断哪棵树合并到另一棵树,并且判断一棵树是否需要升级

代码:

void union_ (int a,int b) {
    int x = find_(a), y = find_(b);
    if (ranks[x] > ranks[y]) {//两棵树一定不同级,不存在升级操作
        bcset[y] = x;
    } else {
        bcset[x] = y;
        if (ranks[x] == ranks[y]) {//两棵同等级的树合并,其中一棵树升级
            ranks[y] ++;
        }
    }
}

你可能感兴趣的:(并查集,算法)