总结&&左偏树

经过一天的学习,总会感觉十分的充实并且会感觉状态非常好,然而今天的考试却让我突然醒悟,认识到自己其实并没有精通。拿到题目,就先浏览了全篇,发现好像自己并不会做任何一道题,就开始放水十分无奈地想题了。想了一会以后,突然发现第一题就是一个线段树,只需要再加一个左区间的右部分和右区间的左部分就行了,这是一道大水水水水水水水水水水水水题呀!于是就满怀激动的开始敲代码。然而在打了一会儿之后,猛地意识到这要加一个lazy标记!这使我十分的震惊,就很无奈的望着自己的代码,又开始加有关lazy的部分。可是就这样2个半小时之后,仍然没调出来,便只好打了个暴力解法交了上去,但最终这个解法也爆零了,让我很桑心,本来思路都已经和标程一模一样了,却由于没有练习足够的版,题而爆零,这已经是一个不容忽视的问题了。

在无奈的打完第一题暴力后,就十分慌乱地开始看后两道题。由于我之前看第二题时,脑子仍然在想第一题,就吧、把第二题想复杂了,一看时间又不够了,就直接跳过看第三题去了。导致我的50分低保也丢了。然而面对今天最难的第三题(。。。)我更加的束手无策,打了个暴力却WA掉了。

这真是悲伤的一天,于是在化悲伤为力量并吃了饱饱的一餐后,我开始学习左偏树,大呼“苟利国家生死以,竹外桃花三两枝,这数据结构竟然如此有用!”,便把它收纳到我的博客里面,好好学习。

左偏树

baidu: 左偏树(Leftist Tree)是一种可并堆的实现。左偏树是一棵二叉树,它的节点除了和二叉树的节点一样具有左右子树指针( left, right)外,还有两个属性,键值和距离(dist)。

(什么乱七八糟的)

还是看点简单易懂的吧。

左偏树的基本性质:
1.堆性质:任意节点的关键字大于等于其孩子节点的关键字(堆性质是为了让最小的结点始终在根的位置,这是所有堆都有的性质。)
2.左偏性质:定义到最近的孩子的距离为节点距离dist,那么任意节点的左孩子的距离大于右孩子的距离(左偏性质是为了让树状存储的堆,树的 深度不能过大,且 利于合并。)
                       即:         A->lchild->dist >= A->rchild->dist
那么这个性质是怎么完成这两个功能的呢?左偏性质使树的左侧的深度始终大于等于右侧的深度,可以画几棵左偏的树试试。而左偏树在实现插入操作时总是从右侧插入,也就是总是让短的一侧生长,如果右侧长于了左侧,那么把左右侧交换一下,继续从短的一侧生长。其实如果不考虑具体的细节,那么这样的直观理解可以看到左偏树的一些本质内涵,一颗树有两个分支,每次要生长的时候,总是让短的一侧先生长,那么这棵树最后就能够比较对称了。

左偏树的4条性质:

(1)节点的键值小于或等于它的左右子节点的键值

(2)节点的左子节点的距离不小于右子节点的距离

(3)节点的距离等于它的右子节点的距离加1

(4)一颗N个节点的左偏树距离最多为log(N+1)-1

其实在画过一颗左偏树并模拟之后,这四条性质也就十分简单易懂了。同时,这四条性质还可以扩展出一些其他的东西,例如:

1. 若左偏树的距离为一定值,则节点数最少的左偏树是完全二叉树。

证明:由性质2可知,当且仅当对于一棵左偏树中的每个节点i都有dist(left(i)) =dist(right(i)) 时,左偏树的节点数最少。而具有这样性质的二叉树是完全二叉树。
2. 若一棵左偏树的距离为k,则这棵左偏树至少有2^(k+1)-1个节点。
证明:由上可知,当这样的左偏树节点数最少的时候是一棵完全二叉树。
           而距离为k的完全二叉树高度也为k,节点数为2^(k+1)-1,所以距离为k的左偏树至少有2^(k+1)-1个节点。
3.(2的推论) 一棵N个节点的左偏树距离最多为ëlog(N+1)û-1。( wtf!反正我目前不知道是什么意思)
证明:设一棵N个节点的左偏树距离为k,由定理1可知,N ≥ 2^(k+1)-1,因此k ≤ ëlog(N+1)û-1。

下面给出左偏树的模板:(这个是copy某位大佬的博客~~)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 10000;
struct Tree{
    int value;
    int dist;
    Tree *left,*right;
};
Tree* tree[MAXN];
int distance(Tree* t){
    return t==NULL?0:t->dist;
}
void fixdist(Tree* t){
    if(distance(t->left) < distance(t->right)){
        swap(t->left,t->right);
    }
    t->dist = distance(t->right)+1;
}
Tree* merge(Tree* a,Tree* b){//合并
    if(a==NULL)return b;
    if(b==NULL)return a;
    if(b->value > a->value){//最大堆
        swap(a,b);
    }
    a->right = merge(a->right,b);
    fixdist(a);
    return a;
}
Tree* delMax(Tree* t){
    if(t!=NULL){
        return merge(t->left,t->right);
    }
    return NULL;
}
void init(Tree* &t,int value){
    t = new Tree;
    t->dist = 1;
    t->value = value;
    t->left = t->right = NULL;
}
Tree* insert(Tree* t,int value){//插入(可以视为合并)
    Tree* p;
    init(p,value);
    return merge(t,p);
}
int main()
{
    ~~~~~~~(根据题目打代码)
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值