1. 程式人生 > >可持久化Treap(fhq Treap,非旋轉式Treap)學習(未完待續)

可持久化Treap(fhq Treap,非旋轉式Treap)學習(未完待續)

efi 最小值 clu oid 遍歷 getch 定義 img element

簡介: 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)學習(未完待續)