码迷,mamicode.com
首页 > 其他好文 > 详细

可持久化Treap(fhq Treap,非旋转式Treap)学习(未完待续)

时间:2017-06-06 10:56:20      阅读:1583      评论:0      收藏:0      [点我收藏+]

标签:红黑树   fine   getch   upd   include   bsp   www   ios   最大值   

简介:
    Treap,一种表现优异的BST
优势:
    其较于AVL、红黑树实现简单,浅显易懂
    较于Splay常数小,通常用于树套BST表现远远优于Splay
    或许有人想说SBT,SBT我没有实现过,据说比较快
    但是SBT、Splay以及旋转版Treap等BST都不可以比较方便地实现‘可持久化操作
 
Treap=Tree+Heap
    Treap是一颗同时拥有二叉搜索树和堆性质的一颗二叉树
    Treap有两个关键字,在这里定义为:
        1.key,满足二叉搜索树性质,即中序遍历按照key值有序
        2.fix,满足堆性质,即对于任何一颗以x为根的子树,x的fix值为该子树的最值,方便后文叙述,定义为最小值
    为了满足期望,fix值是一个随机的权值,用来保证树高期望为logn
    剩下的key值则是用来维护我们想要维护的一个权值,此为一个二叉搜索树的基本要素
 
支持操作:
    基本操作:
        1.Build【构造Treap】【O(n)】
        2.Merge【合并】【O(logn)】
        3.Split【拆分】【O(logn)】
        4.Newnode【新建节点】【O(1)】
    可支持操作:
        1.Insert【Newnode+Merge】【O(logn)】
        2.Delete【Split+Split+Merge】【O(logn)】
        3.Find_kth【Split+Split】【O(logn)】
        4.Query【Split+Split】【O(logn)】
        5.Cover【Split+Split+Merge】【O(logn)】
        and more....
 
只需要两个基础操作,就可以达到splay的所有功能

1: split

将Treap按照权值或排名分裂为两棵Treap 我只写了按权值分裂

对于我们遍历到每一个点,假如它的权值小于k,那么它的所有左子树,都要分到左边的树里,然后遍历它的右儿子。假如大于k,把它的所有右子树分到右边的树里,遍历左儿子。

因为它的最多操作次数就是一直分到底,效率就是O(logn)。

 

技术分享

void split(int now,int k,int &x,int &y)
{
    if(!now) x=y=0;
    else 
    {
        if(val[now]<=k) x=now,split(ch[now][1],k,ch[now][1],y);
        else y=now,split(ch[now][0],k,x,ch[now][0]);
        update(now);
    }
}

2: merge

这个就是把两个Treap合成一个,保证第一个的权值小于第二个。

 

因为第一个Treap的权值都比较小,我们比较一下它的fix(附加权值),假如第一个的fix小,我们就可以直接保留它的所有左子树,接着把第一个Treap变成它的右儿子。反之,我们可以保留第二棵的所有右子树,指针指向左儿子。

你可以把这个过程形象的理解为在第一个Treap的左子树上插入第二个树,也可以理解为在第二个树的左子树上插入第一棵树。因为第一棵树都满足小于第二个树,所以就变成了比较fix来确定树的形态。

也就是说,我们其实是遍历了第一个trep的根->最大节点,第二个Treap的根->最小节点,也就是O(logn)

 

技术分享

 

int merge(int A,int B)
{
    if(!A||!B) return  A+B;
    if(fix[A]<fix[B]){ch[A][1]=merge(ch[A][1],B); update(A); return A;}
    else {ch[B][0]=merge(A,ch[B][0]); update(B); return B;} 
}

 

下面我们就可以通过这两个基本的东西实现各种各样的操作了。

 

3:insertinsert

插入一个权值为v的点,把树按照v的权值split成两个,再按照顺序merge回去。

4: deldel

删除权值为v的点,把树按照v分成两个a,b,再把a按照v-1分成c,d。把c的两个子儿子merge起来,再merge(merge(c,d),b)

(因为把c的两个儿子merge起来之后,如果权值为v的节点有一个,就已经把他删除了,因为merge后c=0;若有多个就删除了一个)

5: precursorprecursor

找前驱的话把root按v-1 split成x,y,在x里面找最大值

6: successorsuccessor

找后继的话把root按v split成x,y,在y里找最小值

7: rankrank

把root按v-1 split成x,y,排名是x的siz

 

代码:https://www.luogu.org/problem/show?pid=3369 普通平衡树

#include<iostream>
#include<cstdio>
#include<cstring>
#include<ctime>
#include<cstdlib>

#define maxn 500001

using namespace std;
int size[maxn],ch[maxn][2],fix[maxn],val[maxn];
int T,cnt,n,m,x,y,z,p,a,root,com;

inline int read()
{
    int x=0,f=1;char c=getchar();
    while(c>9||c<0){if(c==-)f=-1;c=getchar();}
    while(c>=0&&c<=9){x=x*10+c-0;c=getchar();}
    return x*f;
}

inline void update(int x)
{
    size[x]=1+size[ch[x][0]]+size[ch[x][1]];
}
inline int new_node(int x)
{
    size[++cnt]=1;
    val[cnt]=x;
    fix[cnt]=rand();
    return cnt;
}

int merge(int A,int B)
{
    if(!A||!B) return  A+B;
    if(fix[A]<fix[B]){ch[A][1]=merge(ch[A][1],B); update(A); return A;}
    else {ch[B][0]=merge(A,ch[B][0]); update(B); return B;} 
}

void split(int now,int k,int &x,int &y)
{
    if(!now) x=y=0;
    else 
    {
        if(val[now]<=k) x=now,split(ch[now][1],k,ch[now][1],y);
        else y=now,split(ch[now][0],k,x,ch[now][0]);
        update(now);
    }
}

int kth(int now,int k)
{
    while(1)
    {
        if(k<=size[ch[now][0]]) now=ch[now][0];
        else if(k==size[ch[now][0]]+1) return now;
        else k-=size[ch[now][0]]+1,now=ch[now][1];
    }
}

int main()
{
    srand((unsigned)time(NULL));
    T=read();
    while(T--)
    {
        p=read();a=read();
        if(p==1)
        {
            split(root,a,x,y);
            root=merge(merge(x,new_node(a)),y);
        }
        else if(p==2)
        {
            split(root,a,x,z);
            split(x,a-1,x,y);
            y=merge(ch[y][0],ch[y][1]);
            root=merge(merge(x,y),z);
        }
        else if(p==3)
        {
            split(root,a-1,x,y);
            printf("%d\n",size[x]+1);
            root=merge(x,y);
        }
        else if(p==4) printf("%d\n",val[kth(root,a)]);
        else if(p==5)
        {
            split(root,a-1,x,y);
            printf("%d\n",val[kth(x,size[x])]);
            root=merge(x,y);
        }
        else
        {
            split(root,a,x,y);
            printf("%d\n",val[kth(y,1)]);
            root=merge(x,y);
        }
    }
    return 0;
}

 

可持久化Treap(fhq Treap,非旋转式Treap)学习(未完待续)

标签:红黑树   fine   getch   upd   include   bsp   www   ios   最大值   

原文地址:http://www.cnblogs.com/L-Memory/p/6949756.html

(0)
(1)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!