zoukankan      html  css  js  c++  java
  • 可并堆总结

    在了解可并堆之前,我们先复习一下堆的相关知识。堆是一种数据结构,支持插入元素,查询最大值(大根堆),查询最小值(小根堆),删除最大值最小值,稍微改动以后也支持删除任意一个元素,但由于手动实现这样的二叉堆较为复杂,通常情况下我们用系统自带的PQ(priority_queue)来实现普通堆。

    那么可并堆是什么呢?顾名思义,可并堆就是支持合并操作(merge)的一种堆。容易得知,当堆支持合并操作以后,插入元素和删除操作也就会变得十分简单。插入操作,把欲插入的元素当成是一个堆,那么插入操作便转化成了堆的合并操作。删除操作,只需要把根(root)与其左儿子右儿子的边断掉,然后把以左儿子和右儿子为根的两个堆合并,那么我们便从逻辑上把根节点的元素删除了。下面重点来讲解可并堆的实现:

    左偏树是可并堆最好用也是最好写的实现方式。定义一个点的dis值为:从这个点一直沿着右儿子走,最多能够走多少步。记dis[0]=-1。那么dis[x]=dis[rson]+1。那左偏树的定义是什么呢?对于树上的任意一个点,均有 dis[lson]≥dis[rson] ,这样的树即为一棵左偏树。左偏树满足两个性质:

    1.左偏树的任意一棵子树都是左偏树。

    2.对于子数大小为n的点,它的dis值不超过log n。(也是可并堆时间复杂度的保证)
    用左偏树来实现合并操作便简化了许多。
    左偏树的合并是一个递归过程,对于以 x 和 y 为根的树:
    如果 x 和 y 二者之一为空树,则返回另一棵。
    如果 x 和 y 均不为空,则比较 x 和 y 的权值大小。如果 x 的权值较大,那么就把 x 右儿子和 y 合并的结果作为 x 新的右儿子,返回 x ;否则就把x和y交换,继续上面的操作。合并以后由于我们还要维护左偏性质,即:如果合并后右儿子的dis大于左儿子的dis,则交换左右儿子。这便是通过左偏树来实现可并堆的模拟过程。
    代码实现:
    int merge(int x,int y)
    {
        if(!x) return y;
        if(!y) return x;
        if(v[x]<v[y]) swap(x,y);
        r[x]=merge(r[x],y);
        if(d[r[x]]>d[l[x]]) swap(l[x],r[x]);
        d[x]=d[r[x]]+1;
        return x;
    }

    其中v[]表示权值,dis[]定义与上文所述相同,l[]左儿子,r[]右儿子。

    这样我们便实现了堆的合并操作。不难发现,掌握可并堆之后,普通堆便没有了存在的意义。可并堆的时间复杂度比较神奇,合并操作复杂度O(dis[x]+dix[y]),可近似看成O(log n),但不能说可并堆时间复杂度为O(log n),其最坏情况下可以退化到O(n)。

  • 相关阅读:
    第一章 简介(待续)
    第十六章 漫话网站架构师(待续)
    第十五章 网站架构师职场攻略(待续)
    第十四章 架构师领导艺术(待续)
    第十三章 大型网站典型故障分析案例(待续)
    上帝造题的七分钟2/花神游历各国/GSS4 线段树维护区间开方 By cellur925
    LuoguP1606 [USACO07FEB]荷叶塘Lilypad Pond 【最短路】By cellur925
    NOIp2013 车站分级 【拓扑排序】By cellur925
    NOI题库--盒子和小球系列 By cellur925
    关于对动态规划的思考 【转】
  • 原文地址:https://www.cnblogs.com/kanbokedeshiwoerzi/p/8869059.html
Copyright © 2011-2022 走看看