• 斜堆、左偏树简明教程


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

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

     

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

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

     

    斜堆:

    我们将根节点值较大的堆称作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;
    }
    

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

     

  • 相关阅读:
    Spring AOP中文教程《转》
    VBScript 转义字符
    Spring工作原理
    Struts+Hibernate+Spring工作原理及使用理由
    SQL Server中,varchar和nvarchar如何选择?<转>
    TSQL 游标使用
    oracle 与sql server临时表 比较《转》
    Oracle查询语法大全<转>
    B/S 模式 套打 的一种实现方式<转>
    基于Web的套打方案集粹<转>
  • 原文地址:https://www.cnblogs.com/gdc-destinies/p/8377092.html
Copyright © 2020-2023  润新知