1. 程式人生 > >【資料結構】【範浩強treap】

【資料結構】【範浩強treap】

treap其實就是一顆二叉搜尋樹和堆性質的一顆二叉樹。而我要講的這個是不旋轉的treap,它有key值和fix值兩個值,我們要滿足二叉搜尋樹的性質以中序遍歷來按照key值排序,fix值是為了讓樹的高度滿足期望,一個點的fix值比他的兒子都小(或者大),因此fix值應用一個隨機函式刷。不旋轉treap支援以下操作。

    1.Merge【合併】【O(logn)】
    2.Split【拆分】【O(logn)】
    3.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)】

那麼我先說Merge,我們首先要保證fix,那麼接下來我們要保證key值我們保證要合併的倆棵樹前面的一顆樹比後面的樹的所有點的key值都小,那麼如果後面的樹的當前節點的fix更小,我們就把前面的樹的當前節點放在左兒子,如果大就反過來。下面是程式碼。

int Merge(int x,int y)
{
    if(!x||!y) return x+y;
    if(tree[x].fix<tree[y].fix)
    {
        tree[x].ch[1]=Merge(tree[x].ch[1],y);
        Update(x);
        return
x; } else { tree[y].ch[0]=Merge(x,tree[y].ch[0]); Update(y); return y; } }

那個Update是用來更新兒子個數的,後面要用。

void Update(int cur)
{
    tree[cur].sz=1+tree[tree[cur].ch[0]].sz+tree[tree[cur].ch[1]].sz;
}

然後是Split,我們要把一顆樹按照key值分成大於key的樹和小於key的兩棵樹,這個就直接分了吧。。。見程式碼。

void Split
(int now,int k,int &x,int &y) { if(now==0) x=y=0; else { if(tree[now].key<=k) { x=now; Split(tree[now].ch[1],k,tree[now].ch[1],y); } else { y=now; Split(tree[now].ch[0],k,x,tree[now].ch[0]); } Update(now); } }

最後我們總要新建節點吧,那麼就直接新建了吧。

int NewNode(int val)
{
    ncnt++;
    tree[ncnt].ch[0]=tree[ncnt].ch[1]=0;
    tree[ncnt].key=val;
    tree[ncnt].fix=rand();
    tree[ncnt].sz=1;
    return ncnt;
}

我們有了上面的操作之後就可以進行強大的操作了,比如insert我們首先把要插入的節點的val值作為key值把原來的樹分開然後再新建節點,把節點合併放入較小的樹再合併就像這樣。

void insert(int val)
{
    int x,y,z;
    Split(root,val,x,y);
    z=NewNode(val);
    root=Merge(Merge(x,z),y);
}