在上一节中,咱们讲述了Splay的核心操做rotate与splay
本节我会教你们如何用这两个函数实现各类强大的功能
为了方便讲解,咱们拿这道题作例题来慢慢分析html
首先,咱们须要定义一些东西node
struct node { int v;//权值 int fa;//父亲节点 int ch[2];//0表明左儿子,1表明右儿子 int rec;//这个权值的节点出现的次数 int sum;//子节点的数量 }; int tot;//tot表示不算重复的有多少节点
这两个函数就不讲了,前面已经讲的挺详细了c++
根据前面讲的,咱们在插入一个数以后,须要将其旋转到根
首先
当这棵树已经没有节点的时候,咱们直接新建一个节点就好ide
inline int newpoint(int v,int fa)//v:权值;fa:它的爸爸是谁 { tree[++tot].fa=fa; tree[tot].v=v; tree[tot].sum=tree[tot].rec=1; return tot; }
当这可树有节点的时候,咱们根据二叉查找树的性质,不断向下走,直到找到一个能够插入的点,注意在走的时候须要更新一个每一个节点的sum值函数
void Insert(int x) { int now=root; if(root==0) {newnode(x,0);root=tot;}// else { while(1) { T[now].sum++; if(T[now].val==x) {T[now].rec++;splay(now,root);return ;} int nxt=x<T[now].val?0:1; if(!T[now].ch[nxt]) { int p=newnode(x,now); T[now].ch[nxt]=p; splay(p,root);return ; } now=T[now].ch[nxt]; } } }
删除的功能是:删除权值为v的节点
咱们不难想到:咱们能够先找到他的位置,再把这个节点删掉spa
int find(int v) { int now=root; while(1) { if(tree[now].v==v) {splay(now,root);return now;} int nxt=v<tree[now].v?0:1; if(!tree[now].ch[nxt])return 0; now=tree[now].ch[nxt]; } }
这个函数能够找到权值为v的节点的位置,比较好理解,注意别忘记把找到的节点splay到根指针
另外咱们还须要一个完全删除的函数code
update:
这个函数实际上是不须要的,由于后面的节点必定会覆盖前面的节点htm
inline void dele(int x) { tree[x].sum=tree[x].v=tree[x].rec=tree[x].fa=tree[x].ch[0]=tree[x].ch[1]=0; if(x==tot) tot--; }
接下来的任务就是怎么样才能保证删除节点后整棵树还知足二叉查找树的性质
注意:咱们在查找完一个节点的时候已经将他旋转到根了,因此他左边必定都比他小,除此以外没有比他小的节点了(不然还要考虑他父亲比他小的状况)blog
那么此时会出现几种状况
最后把这个节点删掉就好
void delet(int x) { int pos=find(x); if(!pos) return ; if(T[pos].rec>1) {T[pos].rec--,T[pos].sum--;return ;} else { if(!T[pos].ch[0]&&!T[pos].ch[1]) {root=0;return ;} else if(!T[pos].ch[0]) {root=T[pos].ch[1];T[root].fa=0;return ;} else { int left=T[pos].ch[0]; while(T[left].ch[1]) left=T[left].ch[1]; splay(left,T[pos].ch[0]); connect(T[pos].ch[1],left,1); connect(left,0,1);// update(left); } } }
update in 2018.4.10
之前的本身too naive
这个函数这么写就好
int rak(int val) { int pos=find(val); return T[ls(pos)].siz+1; }
这个简单,若是咱们找到了权值为x的节点,那么答案就是他的左子树的大小+1
不然的话根据二叉查找树的性质不断的向下走就能够,注意若是此次是向右走的话答案须要加上它左子树的大小和这个节点的rec值
int rank(int v)// 查询值为v的数的排名 { int ans=0,now=root; while(1) { if(tree[now].v==v) return ans+tree[tree[now].ch[0]].sum+1; if(now==0) return 0; if(v<tree[now].v) now=tree[now].ch[0]; else ans+=tree[tree[now].ch[0]].sum+tree[now].rec,now=tree[now].ch[1]; } if(now) splay(now,root); return 0; }
这个操做就是上面那个操做的逆向操做
用used变量记录该节点以及它的左子树有多少节点
若是x>左子树的数量且< used,那么当前节点的权值就是答案
不然根据二叉查找树的性质继续向下走
一样注意在向右走的时候要更新x
int arank(int x)//查询排名为x的数是什么 { int now=root; while(1) { int used=tree[now].sum-tree[tree[now].ch[1]].sum; if(x>tree[tree[now].ch[0]].sum&&x<=used) break; if(x<used) now=tree[now].ch[0]; else x=x-used,now=tree[now].ch[1]; } splay(now,root); return tree[now].v; }
这个更容易,咱们能够维护一个ans变量,而后对整棵树进行遍历,同时更新ans
int lower(int v)// 小于v的最大值 { int now=root; int ans=-maxn; while(now) { if(tree[now].v<v&&tree[now].v>ans) ans=tree[now].v; if(v>tree[now].v) now=tree[now].ch[1]; else now=tree[now].ch[0]; } return ans; }
这个和上一个同样,就不细讲了
int upper(int v) { int now=root; int ans=maxn; while(now) { if(tree[now].v>v&&tree[now].v<ans) ans=tree[now].v; if(v<tree[now].v) now=tree[now].ch[0]; else now=tree[now].ch[1]; } return ans; }
// luogu-judger-enable-o2 // luogu-judger-enable-o2 #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define ls(x) T[x].ch[0] #define rs(x) T[x].ch[1] #define fa(x) T[x].fa #define root T[0].ch[1] using namespace std; const int MAXN=1e5+10,mod=10007,INF=1e9+10; inline char nc() { static char buf[MAXN],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXN,stdin)),p1==p2?EOF:*p1++; } inline int read() { char c=nc();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=nc();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=nc();} return x*f; } struct node { int fa,ch[2],val,rec,sum; }T[MAXN]; int tot=0,pointnum=0; void update(int x){T[x].sum=T[ls(x)].sum+T[rs(x)].sum+T[x].rec;} int ident(int x){return T[fa(x)].ch[0]==x?0:1;} void connect(int x,int fa,int how){T[fa].ch[how]=x;T[x].fa=fa;} void rotate(int x) { int Y=fa(x),R=fa(Y); int Yson=ident(x),Rson=ident(Y); connect(T[x].ch[Yson^1],Y,Yson); connect(Y,x,Yson^1); connect(x,R,Rson); update(Y);update(x); } void splay(int x,int to) { to=fa(to); while(fa(x)!=to) { int y=fa(x); if(T[y].fa==to) rotate(x); else if(ident(x)==ident(y)) rotate(y),rotate(x); else rotate(x),rotate(x); } } int newnode(int v,int f) { T[++tot].fa=f; T[tot].rec=T[tot].sum=1; T[tot].val=v; return tot; } void Insert(int x) { int now=root; if(root==0) {newnode(x,0);root=tot;}// else { while(1) { T[now].sum++; if(T[now].val==x) {T[now].rec++;splay(now,root);return ;} int nxt=x<T[now].val?0:1; if(!T[now].ch[nxt]) { int p=newnode(x,now); T[now].ch[nxt]=p; splay(p,root);return ; } now=T[now].ch[nxt]; } } } int find(int x) { int now=root; while(1) { if(!now) return 0; if(T[now].val==x) {splay(now,root);return now;} int nxt=x<T[now].val?0:1; now=T[now].ch[nxt]; } } void delet(int x) { int pos=find(x); if(!pos) return ; if(T[pos].rec>1) {T[pos].rec--,T[pos].sum--;return ;} else { if(!T[pos].ch[0]&&!T[pos].ch[1]) {root=0;return ;} else if(!T[pos].ch[0]) {root=T[pos].ch[1];T[root].fa=0;return ;} else { int left=T[pos].ch[0]; while(T[left].ch[1]) left=T[left].ch[1]; splay(left,T[pos].ch[0]); connect(T[pos].ch[1],left,1); connect(left,0,1);// update(left); } } } int rak(int x) { int now=root,ans=0; while(1) { if(T[now].val==x) return ans+T[T[now].ch[0]].sum+1; int nxt=x<T[now].val?0:1; if(nxt==1) ans=ans+T[T[now].ch[0]].sum+T[now].rec; now=T[now].ch[nxt]; } } int kth(int x)//排名为x的数 { int now=root; while(1) { int used=T[now].sum-T[T[now].ch[1]].sum; if(T[T[now].ch[0]].sum<x&&x<=used) {splay(now,root);return T[now].val;} if(x<used) now=T[now].ch[0]; else now=T[now].ch[1],x-=used; } } int lower(int x) { int now=root,ans=-INF; while(now) { if(T[now].val<x) ans=max(ans,T[now].val); int nxt=x<=T[now].val?0:1;//这里须要特别注意 now=T[now].ch[nxt]; } return ans; } int upper(int x) { int now=root,ans=INF; while(now) { if(T[now].val>x) ans=min(ans,T[now].val); int nxt=x<T[now].val?0:1; now=T[now].ch[nxt]; } return ans; } int main() { #ifdef WIN32 freopen("a.in","r",stdin); #else #endif int N=read(); while(N--) { int opt=read(),x=read(); if(opt==1) Insert(x); else if(opt==2) delet(x); else if(opt==3) printf("%d\n",rak(x)); else if(opt==4) printf("%d\n",kth(x)); else if(opt==5) printf("%d\n",lower(x)); else if(opt==6) printf("%d\n",upper(x)); } return 0; }
至此,splay最经常使用的几种函数就解决了,
下面来看几道裸题
不知道为何,个人splay跑的特别快,多是脸太好了吧😂
http://www.cnblogs.com/zwfymqz/p/7896128.html