Splay树
感谢OI-WIKI讲解
1.定义
splay是一种平衡二叉搜索树,由splay操作使时间复杂度O(nlogn)
2.变量
rt根节点编号
tot节点个数计数
tr[].fa父节点编号
tr[].ch[0/1]左右儿子编号
tr[].val该点记录的权值
tr[].cnt该点记录的权值出现次数
tr[].sz子树大小
int rt,tot; struct node{ int fa,ch[2],val,cnt,sz; void clear(){ //delete the node fa=ch[0]=ch[1]=val=sz=cnt=0; } node(){ fa=ch[0]=ch[1]=val=sz=cnt=0; } }tr[200005];
3基本操作
1.maintain,即更新子树大小为当前点cnt与左右节点sz之和。代码如下
2.clear:销毁节点,即清零,代码如上
3.get:判断是否为右儿子,代码如下
void maintain(int x){ //push_up tr[x].sz=tr[tr[x].ch[0]].sz+tr[tr[x].ch[1]].sz+tr[x].cnt; } int get(int x){ //is the right son return x==tr[tr[x].fa].ch[1]; }
4.rotate操作
做父亲的父亲,自己多了一个儿子,父亲少了一个儿子,将一儿子过继给父亲即可
做父亲的父亲,父亲认你当父亲,你就要认爷爷当父亲
void rotate(int x){ int y=tr[x].fa,z=tr[y].fa,ck=get(x),cy=get(y); //get its son to the father's son tr[y].ch[ck]=tr[x].ch[ck^1]; if(tr[x].ch[ck^1])tr[tr[x].ch[ck^1]].fa=y; //be your father's dad tr[x].ch[ck^1]=y; tr[y].fa=x; //be your grandfather's son tr[x].fa=z; if(z)tr[z].ch[cy]=x; //renew the information maintain(x); maintain(y); }
5.splay操作
当所有人的祖先(根节点)
1:父亲是根
rotate一下即可做根
2.你和父亲都是左儿子或都是右儿子
先rotate父亲,做爷爷的父亲,然后自己再做父亲的父亲
3.一个是左儿子,一个是右儿子
rotate两次,即可做爷爷。
void splay(int x){ for(;tr[x].fa;rotate(x)){ int f=tr[x].fa; if(tr[f].fa){ if(get(x)==get(f))rotate(f); //zig-zag else rotate(x); //zig-zig }else{ //only zig } } rt=x; //splay to root }
6.insert操作
以二叉搜索树性质为基础,小了查左儿子,大了查右儿子
相等个数加一,不等新建节点
最后更新sz
void ins(int k){ if(!rt){ tr[++tot].val=k; tr[tot].cnt++; rt=tot; maintain(rt); //the tree is empty so just add it as root return ; } int c=rt,f=0; //c refers to now,while f refers to father while(1){ if(tr[c].val==k){ //find it tr[c].cnt++; maintain(c); //update value directly maintain(f); //to avoid zig-zig make father's data unuseable father should be maintained splay(c); break; } f=c; c=tr[c].ch[k>tr[c].val]; //update the node index and faher index if(!c){ tr[++tot].fa=f; tr[tot].val=k; tr[tot].cnt=1; tr[f].ch[k>tr[f].val]=tot; c=tot; //make a new node maintain(c); maintain(f); //to avoid zig-zig make father's data unuseable father should be maintained splay(c); //to make time ok,need to splay everynode up //can't find it break; } } }
7.rank查询
查询到节点,前面左儿子大小之和+1就是rank
int rk(int k){ int ans=0,nw=rt; //ans means the rank while(1){ if(k<tr[nw].val){ nw=tr[nw].ch[0]; //to left }else{ ans+=tr[tr[nw].ch[0]].sz; if(!nw)return ans+1;//cant find if(tr[nw].val==k){ splay(nw); //splay to root return ans+1; //add itself } ans+=tr[nw].cnt; nw=tr[nw].ch[1]; //to right } } }
8.查询rank为k的数
如果k小于左子树sz就找左子树,否则如果剩下的k小于等于当前节点cnt就是当前节点,否则在右子树。
int kth(int k){ int nw=rt; while(1){ if(tr[nw].ch[0]&&k<=tr[tr[nw].ch[0]].sz){ nw=tr[nw].ch[0]; //k is contained in the left subtree }else{ k-=tr[tr[nw].ch[0]].sz+tr[nw].cnt; if(k<=0){ //k is in the root splay(nw); return tr[nw].val; //splay and answer } nw=tr[nw].ch[1]; //to right } } }
9.合并两个值域不相交的splay(用于删除节点)
将之与较小的树中最大的数splay到根,另一棵树作右子树
int nw=rt,lst=pre();//pre of root tr[tr[nw].ch[1]].fa=lst; tr[lst].ch[1]=tr[nw].ch[1]; //get right son tr[nw].clear(); maintain(rt);
10.删除节点
先splay到根,cnt>1就减少1,如果该树为空则直接删,如果仅有一子树则将根传给该子树,否则合并两棵子树
void del(int k){ rk(k); //splay to root if(tr[rt].cnt>1){ tr[rt].cnt--; //have more than 1 elements maintain(rt); //recalculate return ; } if(tr[rt].ch[0]+tr[rt].ch[1]==0){ //have no son tr[rt].clear(); rt=0; //be an empty tree return ; } if(tr[rt].ch[0]==0){ //to left son int tmp=rt; rt=tr[rt].ch[1]; tr[rt].fa=0; //root dont have dad tr[tmp].clear(); //clear the node return ; } if(tr[rt].ch[1]==0){ //to right son int tmp=rt; rt=tr[rt].ch[0]; tr[rt].fa=0; //root dont have dad tr[tmp].clear(); //clear the node return ; } int nw=rt,lst=pre();//pre of root tr[tr[nw].ch[1]].fa=lst; tr[lst].ch[1]=tr[nw].ch[1]; //get right son tr[nw].clear(); maintain(rt); }
11.查前驱
先插入,前驱即为左子树最靠右的点,之后删除
int pre(){ //first insert,and splay to root int nw=tr[rt].ch[0]; if(!nw)return nw; //it's the smallest number in the tree while(tr[nw].ch[1])nw=tr[nw].ch[1]; //to the right subtree splay(nw); //splay to root return nw; } int ask_pre(int x){ ins(x); int ans=tr[pre()].val; del(x); return ans; }
12.查后继
先插入,后继即为右子树最靠左的点,之后删除
int nxt(){ //first insert,and splay to root int nw=tr[rt].ch[1]; if(!nw)return nw; //it's the largest number in the tree while(tr[nw].ch[0])nw=tr[nw].ch[0]; //to the right subtree splay(nw); //splay to root return nw; } int ask_nxt(int x){ ins(x); int ans=tr[nxt()].val; del(x); return ans; }
13.代码(有注释)
#include <bits/stdc++.h> using namespace std; int rt,tot; struct node{ int fa,ch[2],val,cnt,sz; void clear(){ //delete the node fa=ch[0]=ch[1]=val=sz=cnt=0; } node(){ fa=ch[0]=ch[1]=val=sz=cnt=0; } }tr[200005]; void maintain(int x){ //push_up tr[x].sz=tr[tr[x].ch[0]].sz+tr[tr[x].ch[1]].sz+tr[x].cnt; } int get(int x){ //is the right son return x==tr[tr[x].fa].ch[1]; } void rotate(int x){ int y=tr[x].fa,z=tr[y].fa,ck=get(x),cy=get(y); //get its son to the father's son tr[y].ch[ck]=tr[x].ch[ck^1]; if(tr[x].ch[ck^1])tr[tr[x].ch[ck^1]].fa=y; //be your father's dad tr[x].ch[ck^1]=y; tr[y].fa=x; //be your grandfather's son tr[x].fa=z; if(z)tr[z].ch[cy]=x; //renew the information maintain(x); maintain(y); } void splay(int x){ for(;tr[x].fa;rotate(x)){ int f=tr[x].fa; if(tr[f].fa){ if(get(x)==get(f))rotate(f); //zig-zag else rotate(x); //zig-zig }else{ //only zig } } rt=x; //splay to root } void ins(int k){ if(!rt){ tr[++tot].val=k; tr[tot].cnt++; rt=tot; maintain(rt); //the tree is empty so just add it as root return ; } int c=rt,f=0; //c refers to now,while f refers to father while(1){ if(tr[c].val==k){ //find it tr[c].cnt++; maintain(c); //update value directly maintain(f); //to avoid zig-zig make father's data unuseable father should be maintained splay(c); break; } f=c; c=tr[c].ch[k>tr[c].val]; //update the node index and faher index if(!c){ tr[++tot].fa=f; tr[tot].val=k; tr[tot].cnt=1; tr[f].ch[k>tr[f].val]=tot; c=tot; //make a new node maintain(c); maintain(f); //to avoid zig-zig make father's data unuseable father should be maintained splay(c); //to make time ok,need to splay everynode up //can't find it break; } } } int rk(int k){ int ans=0,nw=rt; //ans means the rank while(1){ if(k<tr[nw].val){ nw=tr[nw].ch[0]; //to left }else{ ans+=tr[tr[nw].ch[0]].sz; if(!nw)return ans+1;//cant find if(tr[nw].val==k){ splay(nw); //splay to root return ans+1; //add itself } ans+=tr[nw].cnt; nw=tr[nw].ch[1]; //to right } } } int kth(int k){ int nw=rt; while(1){ if(tr[nw].ch[0]&&k<=tr[tr[nw].ch[0]].sz){ nw=tr[nw].ch[0]; //k is contained in the left subtree }else{ k-=tr[tr[nw].ch[0]].sz+tr[nw].cnt; if(k<=0){ //k is in the root splay(nw); return tr[nw].val; //splay and answer } nw=tr[nw].ch[1]; //to right } } } int pre(){ //first insert,and splay to root int nw=tr[rt].ch[0]; if(!nw)return nw; //it's the smallest number in the tree while(tr[nw].ch[1])nw=tr[nw].ch[1]; //to the right subtree splay(nw); //splay to root return nw; } int nxt(){ //first insert,and splay to root int nw=tr[rt].ch[1]; if(!nw)return nw; //it's the largest number in the tree while(tr[nw].ch[0])nw=tr[nw].ch[0]; //to the right subtree splay(nw); //splay to root return nw; } void del(int k){ rk(k); //splay to root if(tr[rt].cnt>1){ tr[rt].cnt--; //have more than 1 elements maintain(rt); //recalculate return ; } if(tr[rt].ch[0]+tr[rt].ch[1]==0){ //have no son tr[rt].clear(); rt=0; //be an empty tree return ; } if(tr[rt].ch[0]==0){ //to left son int tmp=rt; rt=tr[rt].ch[1]; tr[rt].fa=0; //root dont have dad tr[tmp].clear(); //clear the node return ; } if(tr[rt].ch[1]==0){ //to right son int tmp=rt; rt=tr[rt].ch[0]; tr[rt].fa=0; //root dont have dad tr[tmp].clear(); //clear the node return ; } int nw=rt,lst=pre();//pre of root tr[tr[nw].ch[1]].fa=lst; tr[lst].ch[1]=tr[nw].ch[1]; //get right son tr[nw].clear(); maintain(rt); } int ask_pre(int x){ ins(x); int ans=tr[pre()].val; del(x); return ans; } int ask_nxt(int x){ ins(x); int ans=tr[nxt()].val; del(x); return ans; } int main(){ int n; cin>>n; while(n--){ int op; cin>>op; if(op==1){ int x; cin>>x; ins(x); }else if(op==2){ int x; cin>>x; del(x); }else if(op==3){ int x; cin>>x; cout<<rk(x)<<'\n'; }else if(op==4){ int k; cin>>k; cout<<kth(k)<<'\n'; }else if(op==5){ int x; cin>>x; cout<<ask_pre(x)<<'\n'; }else{ int x; cin>>x; cout<<ask_nxt(x)<<'\n'; } } }
14.代码(无注释)
#include <bits/stdc++.h> using namespace std; int rt,tot; struct node{ int fa,ch[2],val,cnt,sz; void clear(){ fa=ch[0]=ch[1]=val=sz=cnt=0; } node(){ fa=ch[0]=ch[1]=val=sz=cnt=0; } }tr[200005]; void maintain(int x){ tr[x].sz=tr[tr[x].ch[0]].sz+tr[tr[x].ch[1]].sz+tr[x].cnt; } int get(int x){ return x==tr[tr[x].fa].ch[1]; } void rotate(int x){ int y=tr[x].fa,z=tr[y].fa,ck=get(x),cy=get(y); tr[y].ch[ck]=tr[x].ch[ck^1]; if(tr[x].ch[ck^1])tr[tr[x].ch[ck^1]].fa=y; tr[x].ch[ck^1]=y; tr[y].fa=x; tr[x].fa=z; if(z)tr[z].ch[cy]=x; maintain(x); maintain(y); } void splay(int x){ for(;tr[x].fa;rotate(x)){ int f=tr[x].fa; if(tr[f].fa){ if(get(x)==get(f))rotate(f); else rotate(x); } } rt=x; } void ins(int k){ if(!rt){ tr[++tot].val=k; tr[tot].cnt++; rt=tot; maintain(rt); return ; } int c=rt,f=0; while(1){ if(tr[c].val==k){ tr[c].cnt++; maintain(c); maintain(f); splay(c); break; } f=c; c=tr[c].ch[k>tr[c].val]; if(!c){ tr[++tot].fa=f; tr[tot].val=k; tr[tot].cnt=1; tr[f].ch[k>tr[f].val]=tot; c=tot; maintain(c); maintain(f); splay(c); break; } } } int rk(int k){ int ans=0,nw=rt; while(1){ if(k<tr[nw].val){ nw=tr[nw].ch[0]; }else{ ans+=tr[tr[nw].ch[0]].sz; if(!nw)return ans+1; if(tr[nw].val==k){ splay(nw); return ans+1; } ans+=tr[nw].cnt; nw=tr[nw].ch[1]; } } } int kth(int k){ int nw=rt; while(1){ if(tr[nw].ch[0]&&k<=tr[tr[nw].ch[0]].sz){ nw=tr[nw].ch[0]; }else{ k-=tr[tr[nw].ch[0]].sz+tr[nw].cnt; if(k<=0){ splay(nw); return tr[nw].val; } nw=tr[nw].ch[1]; } } } int pre(){ int nw=tr[rt].ch[0]; if(!nw)return nw; while(tr[nw].ch[1])nw=tr[nw].ch[1]; splay(nw); return nw; } int nxt(){ int nw=tr[rt].ch[1]; if(!nw)return nw; while(tr[nw].ch[0])nw=tr[nw].ch[0]; splay(nw); return nw; } void del(int k){ rk(k); if(tr[rt].cnt>1){ tr[rt].cnt--; maintain(rt); return ; } if(tr[rt].ch[0]+tr[rt].ch[1]==0){ tr[rt].clear(); rt=0; return ; } if(tr[rt].ch[0]==0){ int tmp=rt; rt=tr[rt].ch[1]; tr[rt].fa=0; tr[tmp].clear(); return ; } if(tr[rt].ch[1]==0){ int tmp=rt; rt=tr[rt].ch[0]; tr[rt].fa=0; tr[tmp].clear(); return ; } int nw=rt,lst=pre(); tr[tr[nw].ch[1]].fa=lst; tr[lst].ch[1]=tr[nw].ch[1]; tr[nw].clear(); maintain(rt); } int ask_pre(int x){ ins(x); int ans=tr[pre()].val; del(x); return ans; } int ask_nxt(int x){ ins(x); int ans=tr[nxt()].val; del(x); return ans; } int main(){ int n; cin>>n; while(n--){ int op; cin>>op; if(op==1){ int x; cin>>x; ins(x); }else if(op==2){ int x; cin>>x; del(x); }else if(op==3){ int x; cin>>x; cout<<rk(x)<<'\n'; }else if(op==4){ int k; cin>>k; cout<<kth(k)<<'\n'; }else if(op==5){ int x; cin>>x; cout<<ask_pre(x)<<'\n'; }else{ int x; cin>>x; cout<<ask_nxt(x)<<'\n'; } } }
标签:rt,ch,int,tr,splay,fa,nw From: https://www.cnblogs.com/rabbit-fan/p/18293829