zoukankan      html  css  js  c++  java
  • 斜堆、左偏树简明教程

    在堆的实际应用中,我们经常要做一些堆的合并操作。

    这时候,我们就需要一种新的数据结构:可并堆。

     

    可并堆有很多种,例如:斜堆,左偏树,(配对堆、斐波那契堆,这两个不会说的,因为我不会)。

    这里介绍两种斜堆和左偏树。

     

    斜堆:

    我们将根节点值较大的堆称作a,较小的堆称作b(小根堆相反)。

    那么我们把a的右儿子和b合并作为新堆的右儿子,将a的左儿子作为新堆的左儿子。

    那么我们会遇到这样一个情况,就是随着合并的次数增多,右儿子会变得比较大,而左儿子会变得很小。

    那么我们可以想到一个显而易见的做法,那就是在每次合并后,交换左右儿子,这样下一次合并的时候就会合并到较小的子树上。

    得到了这样的一个算法后,我们来考虑一下复杂度。

    在不加优化的合并方式中,会出现一种情况就是几乎连成一条链,在这种形态下的单次合并复杂度会几乎达到O(N)。

    而我们每次将左右儿子交换,就能保证不会出现这种情况。

    因为事实上每一次新加入的元素是类似平均地加入到这棵树中(因为每次都是加到较小的子树中)。

    这样就能保证合并的复杂度均摊到O(logn)了。

    void merge(int rta,int rtb){
    	if(size[rta]==0)return rtb;
    	if(size[rtb]==0)return rta;
        if(key[rta]<key[rtb])swap(rta,rtb);
        rchild[rta]=merge(rchild[rta],rtb);
    	swap(lchild[rta],rchild[rta]);
        return rta;
    }
    

      

     

    左偏树:

    顺着斜堆的想法,我们可以想到,一次合并的效率与这次合并多快能遇到【一个只有一个儿子的节点】有关(因为在递归合并两棵子树时,若一棵为空,则合并完成)。

    那么我们顺着这个思路,可以定义一个值为与点i最近的【一个只有一个儿子的节点】的距离为dis(i),并且规定每个点左儿子的dis一定比右儿子的dis大,否则交换左右儿子。

    这样每个点的dis就是右儿子dis+1,dis(一个只有一个儿子的节点)=0。

    合并时,与斜堆类似,递归地合并。将根节点值较小的树插到较大的树的右子树里(小根堆相反),在此过程中更新每个点的dis值,并通过交换左右儿子使它满足刚才的性质。

    void merge(int rta,int rtb){
    	if(size[rta]==0)return rtb;
    	if(size[rtb]==0)return rta;
        if(key[rta]<key[rtb])swap(rta,rtb);
        rchild[rta]=merge(rchild[rta],rtb);
        if(dis[lchild[rta]]<dis[rchild[rta]])
    	    swap(lchild[rta],rchild[rta]);
    	dis[rta]=dis[rchild[rta]]+1;
        return rta;
    }
    

    【两种可并堆的除合并以外的操作与堆相同,不再赘述】

     

  • 相关阅读:
    C#调用VC++.net托管库程序集DLL
    SQL Server 2008 评估期已过期解决办法
    TFS2010安装(转)
    将dll添加到GAC(Global Assembly Cache)中
    LINQ to SQL with NOLOCK
    SQL Server 2008创建定期自动备份任务(转)
    问题解决记录(一)
    控制台读入
    数据库
    a标签的onclick与href的执行顺序
  • 原文地址:https://www.cnblogs.com/gdc-destinies/p/8377092.html
Copyright © 2011-2022 走看看