学习左偏树有感//2018/7/1

大佬们请先对并查集略知一二。

今日,学习了传说中的左偏树(虽然感觉学的还是假的)

学习地址:https://www.luogu.org/blog/IAmHellWord/solution-p3377

附加丑代码一段

#include 
using namespace std;

#define C getchar()
#define maxn 101000

inline int read()
{
    int x=0;
    char ch;
    bool flag=true;
    while(!isdigit(ch))
    {
        if(ch=='-')
          flag=false;
        ch=C;
    }
    while(isdigit(ch))
    {
        x=(x<<3)+(x<<1)+(ch^48);
        ch=C;
    }
    return flag?x:-x;
}

struct lefttree
{
    int lson;
    int rson;
    int key;
    int fa;
    int dis;
}tree[maxn]={};
int n,m;

int Un(int u,int v){
    if (!u || !v)return u+v;
    if (tree[u].key>tree[v].key || (tree[u].key==tree[v].key && u>v))swap(u,v);
    int &ul=tree[u].lson,&ur=tree[u].rson;
    ur=Un(ur,v);
    tree[ur].fa=u;
    if (tree[ul].dis

以前仗着有stl库中的堆,打死不学左偏树(反正左偏树除了支持合并以外好像与堆并没有差别),直到碰到了

https://www.luogu.org/problemnew/show/P3377

整个人都不好了。

然后就只有找资料认真学习了,but,为神马网上的blog用的都是指针呢,咱老师说了,用指针的同学特别容易翻车,所以本人就毅然决然地放弃了指针的做法,找到了这么一篇良心题解。

下面就请允许我浅谈一下对左偏树的理解

左偏树,又称可并二叉堆,是基于合并操作的一种数据结构

好了,那么左偏树为什么叫左偏树呢,因为他看起来是向左偏的(我也不知道为什么不规定叫右偏树)

因为他的这一个基本性质,所以也就具备了一下三种特性(以小根堆为例)

1.爸爸节点的权值一定小于等于其左右儿子的权值。

2.左儿子的dis(距离)大于右儿子的dis(dis是此节点到最近叶子结点的距离,即要经过的边的条数)。


3.爸爸节点的dis等于右儿子的dis+1。(废话)

然后根据三个结论,很容易就可以推出一个引理


一个节点个数为n的左偏书的最大距离为log(n+1)-1。

以上便是左偏树的一些基本性质

下面就来讲一些基本操作

第一:合并(代码详见上文自定义函数Un)

这是左偏树操作中最为重要的一个。

他的具体方法为将两棵树一深度较大的那一颗为母树,再将另一棵树合并到母树的右儿子上,然后检查左偏性质,如不满足,交换左右子树,强行使其满足左偏性质。此时由于右儿子可能改变,所以爸爸的深度要改为右儿子的深度+1.

第二:删除最小数(代码详见自定义函数pop)

删除最小数其实就相当于将左右儿子合并,这一个操作是等价于删除爸爸节点的。

第三:查找最小数(代码详见gf)

这里便用到了并查集的思想,由于左偏树的性质一,所以根节点的权值便是最小数。

以上三个蛇皮操作都显示出了左偏树一合并为核心操作的性质,也显示出了左偏树对并查集的依赖性。

本文就这么草草地结束了,欢迎各位dalao在评论区留言指出错误之处或改进建议

转载请标明出处(我相信不会有人转载的)

你可能感兴趣的:(算法)