【資料結構】【範浩強treap】
阿新 • • 發佈:2019-01-28
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);
}