【旋转】
平衡树中的旋转是指在不改变中序遍历的前提下改变树的形态的方式。(中序遍历=排名顺序)
右旋将当前点的左节点旋上来,左旋反之。(图侵删)
void rturn(int &k){
int o=t[k].l;
t[k].l=t[o].r;
t[o].r=k;
up(k);up(o);
k=o;
}
原根k,新根o。
1.把k的左节点o解放出来并更新为o的右节点。
2.解放出来的o成为新根,其右孩子赋为k。
【Treap】树堆
功能:维护支持单点插入和单点删除的排名树。
特点:给每个节点随机堆权,在维持排名不变的前提下通过旋转维护堆结构,从而能使高度平衡。
Treap的左子树<=根节点,右子树>根节点。
操作:
1.查找数的排名或位置:判断与k的大小关系,往左或往右(累加sz),相等时找到数的位置。
2.查找指定排名的数字:判断与t[t[k].l].sz+1的大小关系,往左或往右,相等时找到数的位置。
3.插入:查找数的位置,相等往左走直到空位置插入,回溯时维护堆性质。
4.删除:查找数的位置,相等时比较左右孩子堆权后旋转到底部删除。
5.前驱:查找数的位置,为其左子树的最右孩子,若不存在则为其父亲,若不是父亲的右孩子则无解(第一个数)。
6.后继:同上,右子树的最左孩子,否则为父亲。
注意:要时时维护堆性质,否则速度严重变慢。
例题和模版:【BZOJ】3224: Tyvj 1728 普通平衡树
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=100010; struct tree{int l,r,sz,rnd,num;}t[maxn*2]; int n,sz,root; void up(int k){t[k].sz=1+t[t[k].l].sz+t[t[k].r].sz;} void lturn(int &k){ int o=t[k].r; t[k].r=t[o].l; t[o].l=k; up(k);up(o); k=o; } void rturn(int &k){ int o=t[k].l; t[k].l=t[o].r; t[o].r=k; up(k);up(o); k=o; } void ins(int &k,int x){ if(!k){k=++sz;t[k].rnd=rand();t[k].num=x;t[k].sz=1;return;}//return t[k].sz++; if(x<=t[k].num){ ins(t[k].l,x); if(t[t[k].l].rnd<t[k].rnd)rturn(k);//turn } else{ ins(t[k].r,x); if(t[t[k].r].rnd<t[k].rnd)lturn(k); } } void del(int &k,int x){ //t[k].sz--; if(t[k].num==x){ if(t[k].l*t[k].r==0){k=t[k].l+t[k].r;return;} if(t[t[k].l].rnd<t[t[k].r].rnd){ rturn(k); t[k].sz--; del(t[k].r,x); } else{ lturn(k); t[k].sz--; del(t[k].l,x); } } else if(x<t[k].num)t[k].sz--,del(t[k].l,x);else t[k].sz--,del(t[k].r,x); } int find(int k,int x){ if(!k)return 0;//!k if(x<=t[k].num)return find(t[k].l,x); else return t[t[k].l].sz+1+find(t[k].r,x); } int rank(int k,int x){ if(t[t[k].l].sz+1==x)return t[k].num; if(x<t[t[k].l].sz+1)return rank(t[k].l,x); else return rank(t[k].r,x-t[t[k].l].sz-1); } int pre(int k,int x){ if(!k)return -1; if(t[k].num<x)return max(t[k].num,pre(t[k].r,x)); else return pre(t[k].l,x); } int suc(int k,int x){ if(!k)return 0x3f3f3f3f; if(t[k].num>x)return min(t[k].num,suc(t[k].l,x)); else return suc(t[k].r,x); } int main(){ srand(233);//srand!!!!!!!!!! scanf("%d",&n);sz=root=0; for(int i=1;i<=n;i++){ int opt,x; scanf("%d%d",&opt,&x); if(opt==1)ins(root,x); if(opt==2)del(root,x); if(opt==3)printf("%d\n",find(root,x)+1);//+1 if(opt==4)printf("%d\n",rank(root,x)); if(opt==5)printf("%d\n",pre(root,x)); if(opt==6)printf("%d\n",suc(root,x)); } return 0; }