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);
}