0x46蓝书习题:普通平衡树
Treap/平衡二叉树
蓝书习题:普通平衡树
这道题是一道平衡树模板题,可以用多种解法,这里用最简单的Treap,下面简单说一下各种操作的思路
-
添加:
当要添加一个值时,先判断所要加入的以p为根节点的子树是否为空,为空添加新的节点:New(val)。 当然平衡树,当加入新节点后,子节点dat变得大于a[p].dat时,要旋转,左旋右旋见代码,好理解。void Insert(int &p,int val){ if(!p){ p=New(val); return; } if(val==a[p].val){ a[p].cnt++,Updata(p); return; } if(val<a[p].val){ Insert(a[p].l,val); if(a[p].dat<a[a[p].l].dat) zig(p); } else{ Insert(a[p].r,val); if(a[p].dat<a[a[p].r].dat) zag(p); } Updata(p); }
-
删除 :
平衡树删除要容易,就是将要删除的节点不停旋转到叶子上,再删掉。见代码void Remove(int &p,int val){ if(p==0) return; if(val==a[p].val){ if(a[p].cnt>1){ //val这个值有重复,直接减少cnt即可 a[p].cnt--,Updata(p); return; } if(a[p].l||a[p].r){ //p有子树,不是叶子 if(a[p].r==0||a[a[p].l].dat > a[a[p].r].dat) zig(p),Remove(a[p].r,val); //左子树长于右子树,向右旋 else zag(p),Remove(a[p].l,val); Updata(p); } else p=0; //p是叶子直接删除 return; } if(val<a[p].val) Remove(a[p].l,val); else Remove(a[p].r,val); Updata(p); }
-
输出一个值的排名:
根据平衡二叉树的性质,最左边的值最小,那么如果我知道p的左子树siz,那么p的排名就是 a[a[p].l].siz+1,根据这一点。如果val<a[p].val,说明val在左子树中,递归处理左子树。当val>a[p].val,时,递归右子树时返回值要加上p左子树的siz和p的重复次数cnt这是由于递归右子树返回值,表示的是val在右子树中的排名,但是易知,val大于左子树和p的val。int GetRankByVal(int p,int val){ if(p==0) return 0; if(val==a[p].val) return a[a[p].l].siz+1; else if(val<a[p].val) return GetRankByVal(a[p].l,val); return GetRankByVal(a[p].r,val)+a[a[p].l].siz+a[p].cnt; }
-
输出某排名的值:
这个看代码吧。int GetvalByRank(int p,int rak){ if(p==0) return INF; if(a[a[p].l].siz>=rak) return GetvalByRank(a[p].l,rak); if(a[a[p].l].siz+a[p].cnt>=rak) return a[p].val; return GetvalByRank(a[p].r,rak-a[a[p].l].siz-a[p].cnt); } //2.左子树siz>rak,说明排名为rak的节点在左子树中 //3.左子树的siz+p点的cnt>rank,而且左子树的siz<rak,说明排名为rak的就是p点的val //4.其他就,递归处理右子树,注意要减去左子树siz和p点的重复值
-
找前驱/后继:
前驱是比val小的最大值 ,后继是比val大的最小值**int GetPre(int val){ int ans=1; int p=root; while(p){ if(a[p].val==val){ if(a[p].l>0){ p=a[p].l; while(a[p].r>0) p=a[p].r; ans=p; } break; } if(a[p].val<val&&a[p].val>a[ans].val) ans=p; p= val<a[p].val ? a[p].l : a[p].r; } return a[ans].val; }
int GetNext(int val){ int ans=2; int p=root; while(p){ if(val==a[p].val){ if(a[p].r){ p=a[p].r; while(a[p].l>0)p=a[p].l; ans=p; } break; } if(a[p].val>val&&a[p].val<a[ans].val) ans=p; p= val<a[p].val ? a[p].l : a[p].r; } return a[ans].val; }
完整代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MA=1e5+5;
const int INF=0x3f3f3f3f;
struct Treap
{
int l,r;
int val,dat; //val:权值,dat:随机值,用来判断左右旋
int cnt,siz; //cnt:记录这个权值的重复次数,siz:子树大小
}a[MA];
int tot,n,root;
int New(int val){
a[++tot].val=val;
a[tot].siz=a[tot].cnt=1;
a[tot].dat=rand();
return tot;
}
void Updata(int p){
a[p].siz=a[a[p].l].siz+a[a[p].r].siz+a[p].cnt;
}
void Build(){
New(-INF),New(INF);
root=1,a[1].r=2;
Updata(root);
}
void zig(int &p){
int q=a[p].l;
a[p].l=a[q].r,a[q].r=p;
p=q;
Updata(a[p].r);
Updata(p);
}
void zag(int &p){
int q=a[p].r;
a[p].r=a[q].l,a[q].l=p;
p=q;
Updata(a[p].l);
Updata(p);
}
void Insert(int &p,int val){
if(!p){
p=New(val);
return;
}
if(val==a[p].val){
a[p].cnt++,Updata(p);
return;
}
if(val<a[p].val){
Insert(a[p].l,val);
if(a[p].dat<a[a[p].l].dat) zig(p);
}
else{
Insert(a[p].r,val);
if(a[p].dat<a[a[p].r].dat) zag(p);
}
Updata(p);
}
void Remove(int &p,int val){
if(p==0) return;
if(val==a[p].val){
if(a[p].cnt>1){ //val这个值有重复,直接减少cnt即可
a[p].cnt--,Updata(p);
return;
}
if(a[p].l||a[p].r){ //p有子树,不是叶子
if(a[p].r==0||a[a[p].l].dat > a[a[p].r].dat) zig(p),Remove(a[p].r,val); //左子树长于右子树,向右旋
else zag(p),Remove(a[p].l,val);
Updata(p);
}
else p=0; //p是叶子直接删除
return;
}
if(val<a[p].val) Remove(a[p].l,val);
else Remove(a[p].r,val);
Updata(p);
}
int GetRankByVal(int p,int val){
if(p==0) return 0;
if(val==a[p].val) return a[a[p].l].siz+1;
else if(val<a[p].val) return GetRankByVal(a[p].l,val);
return GetRankByVal(a[p].r,val)+a[a[p].l].siz+a[p].cnt;
}
int GetvalByRank(int p,int rak){
if(p==0) return INF;
if(a[a[p].l].siz>=rak) return GetvalByRank(a[p].l,rak); //左子树siz>rak,说明排名为rak的节点在左子树中
if(a[a[p].l].siz+a[p].cnt>=rak) return a[p].val; //左子树的siz+p点的cnt>rank,而且左子树的siz<rak,说明排名为rak的就是p点的val
return GetvalByRank(a[p].r,rak-a[a[p].l].siz-a[p].cnt); //其他就,递归处理右子树,注意要减去左子树siz和p点的重复值
}
int GetPre(int val){
int ans=1;
int p=root;
while(p){ //子树为处理完
if(a[p].val==val){ //找到val的节点
if(a[p].l>0){ //如果p有左子树,那就让p->左子树,然后一直向右子树移动,最后得到的p就是小于val的最大值
p=a[p].l;
while(a[p].r>0) p=a[p].r;
ans=p;
}
break;
}
if(a[p].val<val&&a[p].val>a[ans].val) ans=p; //用p的值更新ans,找小于val的最大值
p= val<a[p].val ? a[p].l : a[p].r; //递归合适的子树
}
return a[ans].val;
}
int GetNext(int val){
int ans=2;
int p=root;
while(p){
if(val==a[p].val){
if(a[p].r){
p=a[p].r;
while(a[p].l>0)p=a[p].l;
ans=p;
}
break;
}
if(a[p].val>val&&a[p].val<a[ans].val) ans=p;
p= val<a[p].val ? a[p].l : a[p].r;
}
return a[ans].val;
}
int main()
{
Build();
scanf("%d",&n);
while(n--){
int opt,x;
scanf("%d%d",&opt,&x);
if(opt==1) Insert(root,x);
else if(opt==2) Remove(root,x);
else if(opt==3) printf("%d\n",GetRankByVal(root,x)-1);
else if(opt==4) printf("%d\n",GetvalByRank(root,x+1));
else if(opt==5) printf("%d\n",GetPre(x));
else if(opt==6) printf("%d\n",GetNext(x));
}
return 0;
}