相比其他平衡树,非旋Treap看起来更加简洁,而且也去掉了玄学的rotate,并加入了merge(合并),split(分裂)
我们看一下非旋Treap的基本操作
分裂操作是按照权值的大小,将一棵树分裂成<=val和>val两棵树,这样可以实现许多操作
inline void split(int now,int k,int &x,int &y)
{
if (!now) x=y=0;//叶子节点
else
{
if (val[now]<=k)//将主树与右子树分离
x=now,split(rs[now],k,rs[now],y);
else //将主树与左子树分离
y=now,split(ls[now],k,x,ls[now]);
update(now);
}
return ;
}
我们按照分裂的思路,把两棵树合并到一起,也是和普通平衡树一样,按照根节点的rand值,决定如何合并两棵树
inline int merge(int x,int y)
{
if (!x||!y) return x+y;
if (rd[x]
加入新节点的操作就用到了上面两个操作的结合
我们以要插入的点的权值val为分界线,将树划分成两棵树,那么一棵树(左树)中的点的权值都<=val,另一棵树(右树)都>val
那么将新节点看做一棵树,先和左树合并,显然符合平衡树性质,然后将这棵新树和右树合并,这样就得到了一棵nice的新树
据说和普通Treap形态是一样的
inline int newnode(int x)
{
siz[++sz]=1;val[sz]=x;rd[sz]=rand();
return sz;
}//加入新节点
split(rt,a,x,y);//以a为界限,划分为x和y两棵树
rt=merge(merge(x,newnode(a)),y);//先将a看做一棵树与x合并,再和y合并
删除节点也用到了分裂和合并,我们以要删除的点的权值为分界,划分成两棵树,大于节点权值的点都在右树,左树上都小于等于a的权值,之后将左树按照a的权值-1再划分一次,新右树的根节点就是a,然后合并他的左右儿子,他就被删除了,然后将这三棵树合并,就得到了一棵nice的删除了a的新树
split(rt,a,x,z);split(x,a-1,x,y);//将树按a划分成两棵树,这时a在x树上,且为最大值 && 将x树按a-1划分成两棵树,这时a一定是y树的根节点
y=merge(ls[y],rs[y]);//然后合并a的左右儿子,那么a就被删除了
rt=merge(merge(x,y),z);//然后合并三棵树
根据刚刚删除节点的操作,我们按a-1将树划分成两棵树,那么左树中的最大值小于等于a-1,右树大于等于a,那么a的排名就是左树的siz+1
{
split(rt,a-1,x,y);
printf("%d\n",siz[x]+1);
rt=merge(x,y);
}//查询排名,按a-1划分成两棵树,x树中的都小于a,所以是x树的节点数+1
这个操作就和普通Treap一样了,根据Treap的性质递归实现
先判断k与x的左子树的大小关系,小于等于说明在左子树,k=左子树+1,那么x就是ans,否则递归到右子树
inline int kth(int now,int k)
{
while (1)
{
if (k<=siz[ls[now]]) now=ls[now]; //在左子树中
else if (k==siz[ls[now]]+1) return now;//get到答案
else k-=siz[ls[now]]+1,now=rs[now]; //在右子树中
}
return 0;
}
printf("%d\n",val[kth(rt,a)]);
用到了排名查询和分裂,先按a-1将树分为两棵树,那么左树中的权值都小于a,那么左树中最大的就是a的前驱,也就是左树中排名为左树大小的元素
split(rt,a-1,x,y);//先按a-1划分成两棵树,x树中的数都小于等于a-1,那么排名为siz[x]的数就是最大的
printf("%d\n",val[kth(x,siz[x])]);
rt=merge(x,y);
原理和前驱相同,按照a将树划分成两棵树,那么右树中最小的就是a的后继,也就是右树中排名为1的元素
split(rt,a,x,y);//原理同前驱
printf("%d\n",val[kth(y,1)]);
rt=merge(x,y);
//Treap By AcerMo
#include
#include
#include
#include
#include
#include
#include
using namespace std;
const int M=100500;
int n,sz;
int ls[M],rs[M],val[M],rd[M],siz[M];
inline int read()
{
int x=0,f=1;char ch=getchar();
while (ch>'9'||ch<'0') {if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline void update(int x)
{
siz[x]=siz[ls[x]]+1+siz[rs[x]];
return ;
}
inline int newnode(int x)
{
siz[++sz]=1;val[sz]=x;rd[sz]=rand();
return sz;
}//加入新节点
inline int merge(int x,int y)
{
if (!x||!y) return x+y;
if (rd[x]