日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

Treap

發布時間:2024/8/26 综合教程 33 生活家
生活随笔 收集整理的這篇文章主要介紹了 Treap 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Treap=Tree+Heap,即在普通二叉查找樹的基礎上每個節點有了一個新值域:強化值(因為它將普通二叉查找樹強化為treap就自己起了這個名字,是用來滿足堆性質的,即后文說滿足堆性質都指強化值滿足堆性質)。要求這個樹節點的鍵值(即要代表的數)滿足BST的性質、強化值滿足小跟堆的性質(你非得大根堆也沒什么)。強化值由建立節點時由一個隨機算法(rand())給出,在一個以隨機數據建成的堆的加持下,treap的期望高度被證明為logn,故是一個平衡樹。

代碼請看文末

核心操作:旋轉

  左旋(zig):左旋一棵子樹,它的根變為新子樹的左兒子、根的右兒子變為新子樹的根,那么根的右兒子的新左兒子是根了,原來的左兒子怎么辦?交給根剛好。這樣操作會發現,BST的性質仍然滿足(相對左右位置未變),整個樹宏觀上向左“轉動”了一下。

  右旋(zig):右旋一棵子樹,它的根變為新子樹的右兒子、根的左兒子變為新子樹的根,根的右兒子的原左兒子也可交給根。這樣操作會發現,BST的性質仍然滿足(相對左右位置未變),整個樹宏觀上向右“轉動”了一下。

  故:旋轉不改變BST性質,但會改變父祖關系。同時不改變如圖x、y、z三部分的堆性質(不特指某種旋轉。紅點為即將認父的根,綠點為即將認兒的子節點。x為會變為綠點的外側子樹,z代表原根的另一個子樹,y代表內側綠點會給紅點的子樹)

泛用操作:

  1、插入x數:

    按照普通二叉查找樹插入方式新建節點后使其獲得強化值。回溯過程中看兒子:左兒子強化值小于自己——右旋(讓他當新爹,小根堆嘛);右兒子強化值小于自己——左旋。

    解答為何用旋轉方式維護堆性質:首先旋轉不改變BST性質,但可以改變父子關系。看上圖,若綠點強化值小于紅點,有堆性質得,綠點要當紅點父親才行。一開始綠點子樹一定是滿足堆性質的(只有它一個點),因為綠點強化值小于紅點,所以綠點完全可以當紅點子樹的根。由于紅點子樹在插入值前滿足堆性質,而綠點一定是旋轉上來的,所以紅點可以當除綠點外子樹所有點的父親/祖先,故旋轉完成后,紅點為首的子樹變為綠點為首的子樹,同時整個子樹都滿足堆性質了。

  2、刪除x數:

    思路類似普通二叉查找樹,找到節點后,若cnt(為了考慮重復值而設的)>1,則cnt--,否則,若:

      其最多只有一個子節點:讓子節點代替他(若有的話),若沒有,直接刪就好。

      有兩個子節點:旋轉,讓強化值小的子節點當新爹,把原爹(即要刪的)旋下去,直到刪它的情況變為第一種。

        解答這里為何旋轉:強化值小的子節點可以當原子樹內除原爹外所有點的父親/祖先,旋轉后子樹不含原爹為首新子樹的部分仍滿足堆性質。以原爹為首的新子樹除了原爹,強化值最小的(即原爹的原另一個強化值較大的子節點)點也沒原爹現在的新爹(即原爹的原強化值較小的子節點)小,故可預知刪除原爹后,整個樹仍滿足堆性質。

  3、查詢x數的排名  4、查詢排名為x的數  5、求x的前驅  6、求x的后繼 這些操作與二叉查找樹的操作無異,見:二叉查找樹

  7、分離:

    要把一個Treap按大小分成兩個Treap,即大于等于x的分離成一個treap,剩下的也成一個treap,只要加一個虛擬節點(在需要分開的兩點間,或某個葉子結點的兒子,看實際情況。怎么找?前驅或后繼),然后將虛擬節點旋至根節點刪除,左右兩個子樹就是得出的兩個Treap了。根據二叉排序樹的性質,這時左子樹的所有節點都小于右子樹的節點。時間相當于一次插入操作的復雜度,也就是 log( n )

  8、合并:

    合并是指把兩個Treap合并成一個Treap,本文指其中第一個Treap的所有節點都必須小于或等于第二個Treap中的所有節點,先不涉及兩個普通treap的合并。只要加一個虛擬的根,把兩棵樹分別作為左右子樹,然后把根刪除就可以了。時間復雜度和刪除一樣,也是期望O(log n)

練習題:

  洛谷P3369 【模板】普通平衡樹

#include<iostream>
#include<cstdio>
#include<algorithm>
//#include<cstdlib>
using namespace std;

const int N=100005;

int n,root,bnt;

struct node{
    int ls,rs,cnt,siz,val,dev;
}tre[N];

char ch;

bool f;

inline int read()
{
    int x=0;
    f=0;
    ch=getchar();
    while(!isdigit(ch))
        f|=ch=='-',ch=getchar();
    while(isdigit(ch))
        x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return f?-x:x;
}

inline void upt(int u)
{
    tre[u].siz=tre[tre[u].ls].siz+tre[tre[u].rs].siz+tre[u].cnt;
}

inline void zig(int &u)
{
    int v=tre[u].rs;
    tre[u].rs=tre[v].ls;
    tre[v].siz=tre[u].siz;
    tre[v].ls=u;
    upt(u);
    u=v;
}

inline void zag(int &u)
{
    int v=tre[u].ls;
    tre[u].ls=tre[v].rs;
    tre[v].siz=tre[u].siz;
    tre[v].rs=u;
    upt(u);
    u=v;
}

void insert(int &u,const int &val)
{
    if(!u)
    {
        u=++bnt;
        tre[u].val=val;
        tre[u].cnt=tre[u].siz=1;
        tre[u].dev=rand();
        return;
    }
    tre[u].siz++;
    if(tre[u].val==val)
    {
        tre[u].cnt++;
        return;
    }
    if(val<tre[u].val)
    {
        insert(tre[u].ls,val);
        if(tre[tre[u].ls].dev<tre[u].dev)
            zag(u);
    }
    else
    {
        insert(tre[u].rs,val);
        if(tre[tre[u].rs].dev<tre[u].dev)
            zig(u);
    }
}

void del(int &u,const int &val)
{
    if(!u)    
        return;
    if(tre[u].val==val)
    {
        if(tre[u].cnt>1)
        {
            tre[u].cnt--;
            tre[u].siz--;
        }
        else
        {
            if(tre[u].ls&&tre[u].rs)
            {
                if(tre[tre[u].ls].dev<=tre[tre[u].rs].dev)
                {
                    zag(u);
                    tre[u].siz--;
                    del(tre[u].rs,val);
                }
                else
                {
                    zig(u);
                    tre[u].siz--;
                    del(tre[u].ls,val);
                }
            }
            else 
                if(tre[u].ls||tre[u].rs)
                    u=tre[u].ls+tre[u].rs;
                else
                    u=0;
        }
        return;
    }
    tre[u].siz--;
    if(val<tre[u].val)
        del(tre[u].ls,val);
    else
        del(tre[u].rs,val);
}

int finrank(const int &u,const int &val)
{
    if(!u)
        return 1;
    if(tre[u].val==val)
        return tre[tre[u].ls].siz+1;
    if(val<tre[u].val)
        return finrank(tre[u].ls,val);
    else
        return finrank(tre[u].rs,val)+tre[tre[u].ls].siz+tre[u].cnt;
}

int finval(const int &u,const int &rnk)
{
    if(rnk<=tre[tre[u].ls].siz)
        return finval(tre[u].ls,rnk);
    if(rnk>tre[tre[u].ls].siz+tre[u].cnt)
        return finval(tre[u].rs,rnk-tre[tre[u].ls].siz-tre[u].cnt);
    return tre[u].val;
}

int finpre(const int &u,const int &val)
{
    if(!u)
        return -99999999;
    if(tre[u].val<val)
    {
        int k=finpre(tre[u].rs,val);
        return max(k,tre[u].val);
    }
    else
        return finpre(tre[u].ls,val);
}

int finnxt(const int &u,const int &val)
{
    if(!u)
        return 99999999;
    if(tre[u].val>val)
    {
        int k=finnxt(tre[u].ls,val);
        return min(k,tre[u].val);
    }
    else
        return finnxt(tre[u].rs,val);
}

int main()
{
    srand(9999);
    int n=read();
    int opt,x;
    while(n--)
    {
        opt=read(),x=read();
        switch(opt)
        {
            case 1:
                insert(root,x);
                break;
            case 2:
                del(root,x);
                break;
            case 3:
                printf("%d
",finrank(root,x));
                break;
            case 4:
                printf("%d
",finval(root,x));
                break;
            case 5:
                printf("%d
",finpre(root,x));
                break;
            case 6:
                printf("%d
",finnxt(root,x));
                break;
        }
    }
    return 0;
}

既是示例代碼也是題解

結語:維持Treap的平衡性,強化值有著決定性的作用,故有時臉黑TLE也不是沒有可能的。。。

補充:普通treap是不能O(log n)做區間操作的。為什么?因為你對al~ar沒法快速標記。如確實想用treap做區間操作,移步fhq treap。因為fhq treap可將一個區間內的點全都裂成一個樹,給這個樹根打標記就完成了對整個區間的標記。

EX:洛谷日報:不用旋轉的treap?——fhq treapbug賊多不推薦了

參考資料:

Treap百度百科

總結

以上是生活随笔為你收集整理的Treap的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。