0
点赞
收藏
分享

微信扫一扫

algorithm: Treap 平衡二叉搜索树


Treap是平衡二叉搜索树的一种实现,Treap是Tree和Heap的合成,既有二叉查找树BST的性质,又有堆的性质,所以既能维护排名,又保证是在O(logN)的深度。
BST,即二叉查找树,是指对于任意节点,保证根左侧子树的所有节点比根小,右侧的所有节点比根大的树(没有相同节点)。
而二叉搜索树很容易不平衡,例如不断插入一个递减的数组,则形成的二叉树是线性的。
利用堆的性质,赋予每一个节点一个随机值,按照随机值维护堆的形状。于是我们需要一个操作,既能保持BST的性质,又能够将根节点与儿子替换,于是我们需要Treap的核心——旋转操作(具体看代码注释)。
代码如下:
利用地址的方式存储子节点的方式很巧妙,需要好好理解下。

using namespace std;
typedef long long LL;
const int INF=1e9+7, MAXN=1e5+10;
int sz, rt;
/*
sz 总的节点数
rt 根
siz[i]表示以i为根的节点数
key[i]表示i节点的关键字
cnt[i]表示i节点的重复数
rd[i]i节点的随机值,在堆中的关键字
*/
int siz[MAXN], key[MAXN], cnt[MAXN], rd[MAXN], son[MAXN][2];

inline void push_up(int x){
siz[x]=siz[son[x][0]]+siz[son[x][1]]+cnt[x];
}
/*
x为旋转的根节点,y=0左旋转 y=1右旋转
*/
void rotate(int &x, int y){
int ii=son[x][y^1]; //旋转根节点x 以y=0 左旋转为例,先保存右节点
son[x][y^1]=son[ii][y]; //将根节点的右节点赋予右节点的左节点
son[ii][y]=x; //将根节点赋到右节点的左子节点上
push_up(x);//更新size,先更新子节点,再更新父节点
push_up(ii);
x=ii;//将右节点的值赋给了x地址,x地址最开始都来自son数组或rt
}
/* 插入节点 */
void ins(int &p, int x){
//p==0 空节点,则p=++sz p就是节点给p赋一个唯一索引
//节点的性质通过siz[p] cnt[p]获取
if(!p){
p=++sz;
siz[p]=cnt[p]=1;
key[p]=x;//插入节点值
rd[p]=rand();
return;
}
if(key[p]==x){
cnt[p]++;
siz[p]++;
return;
}
int d=(x>key[p]);
ins(son[p][d],x);//注意传入的是地址
if(rd[p]<rd[son[p][d]]) rotate(p,d^1);//维护平衡
push_up(p);
}

/* 删除节点 */
void del(int &p, int x){
if(!p) return;
if(x!=key[p]){
del(son[p][x>key[p]], x);
}else{
if(!son[p][0]&&!son[p][1]){
siz[p]--;
cnt[p]--;
if(cnt[p]==0) p=0;//不存在该节点,也不存在子节点,删除该节点
}else if (son[p][0]&&!son[p][1]){
rotate(p,1);
del(son[p][1],x);
}else if(!son[p][0]&&son[p][1]){
rotate(p,0);
del(son[p][0],x);
}else{
int d=rd[son[p][0]]>rd[son[p][1]];
rotate(p,d);//d=1,左节点rd大,则右旋转
del(son[p][d],x);//旋转后原来的根在右节点,所以删除右节点
}
}
push_up(p);
}
/*查询排名*/
int rrank(int p,int x){
if(!p) return 0;
if(key[p]==x) return siz[son[p][0]]+1;
else if (key[p]<x){
return siz[son[p][0]] + cnt[p] + rrank(son[p][1],x);
}else{
return rrank(son[p][0],x);
}
}
/*按排名查询值 查询根p下排名为x的数*/
int find(int p, int x){
if(!p) return 0;
if(x<=siz[son[p][0]]) return find(son[p][0],x);
else if (x<=siz[son[p][0]]+cnt[p]) return key[p];
else return find(son[p][1],x-siz[son[p][0]]-cnt[p]);
}
/*查询根为p的子树中x的前驱*/
int pre(int p,int x){
if(!p) return -INF;//便于不满足条件时取max
if(key[p]>=x) return pre(son[p][0],x);
else{
return max(key[p],pre(son[p][1],x));
}
}
/*查询根为p的子树中x的后继*/
int suf(int p,int x){
if(!p) return INF;//便于无结果时取min
if(key[p]<=x) return suf(son[p][1],x);
else{
return min(key[p],suf(son[p][0],x));
}
}
int Q;
int main(){
scanf("%d", &Q);
while (Q--) {
int ii,jj;
scanf("%d%d",&ii,&jj);
switch (ii) {
case 1:
ins(rt,jj);
break;
case 2:
del(rt,jj);
break;
case 3:
printf("%d\n",rrank(rt, jj));
break;
case 4:
printf("%d\n",find(rt,jj));
break;
case 5:
printf("%d\n",pre(rt,jj));
break;
case 6:
printf("%d\n",suf(rt,jj));
break;
default:
Q=0;
break;
}
}
}

​​Treap介绍​​


举报

相关推荐

0 条评论