标签:har ace ref 二叉查找树 org connect 定义 简单 roo
在上一节中,我们讲述了Splay的核心操作rotate与splay
本节我会教大家如何用这两个函数实现各种强大的功能
为了方便讲解,我们拿这道题做例题来慢慢分析
首先,我们需要定义一些东西
struct node
{
int v;//权值
int fa;//父亲节点
int ch[2];//0代表左儿子,1代表右儿子
int rec;//这个权值的节点出现的次数
int sum;//子节点的数量
};
int pointnum,tot;//pointnum代表算上重复的有多少节点,tot表示不算重复的有多少节点
这两个函数就不讲了,前面已经讲的挺详细了
根据前面讲的,我们在插入一个数之后,需要将其旋转到根
所以insert函数可以这么写
inline void insert(int v)
{
int p=build(v);//p代表插到了哪里
splay(p,root);
}
那么build函数怎么写呢?
当这棵树已经没有节点的时候,我们直接新建一个节点就好
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值
int build(int v)
{
pointnum++;
if(tot==0){root=1;newpoint(v,0);}
else
{
int now=root;
while(1)
{
tree[now].sum++;
if(tree[now].v==v){tree[now].rec++;return now;}//出现过
int nxt=v<tree[now].v?0:1;
if(!tree[now].ch[nxt])
{
newpoint(v,now);
tree[now].ch[nxt]=tot;
return tot;
}
now=tree[now].ch[nxt];
}
}
return 0;
}
删除的功能是:删除权值为v的节点
我们不难想到:我们可以先找到他的位置,再把这个节点删掉
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到根
另外我们还需要一个彻底删除的函数
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--;
}
接下来的任务就是怎么样才能保证删除节点后整棵树还满足二叉查找树的性质
注意:我们在查找完一个节点的时候已经将他旋转到根了,所以他左边一定都比他小,除此之外没有比他小的节点了(否则还要考虑他父亲比他小的情况)
那么此时会出现几种情况
最后把这个节点删掉就好
void pop(int v)
{
int deal=find(v);
if(!deal) return ;
pointnum--;
if(tree[deal].rec>1){tree[deal].rec--;tree[deal].sum--;return ;}
if(!tree[deal].ch[0]) root=tree[deal].ch[1],tree[root].fa=0;
else
{
int le=tree[deal].ch[0];
while(tree[le].ch[1]) le=tree[le].ch[1];
splay(le,tree[deal].ch[0]);
int ri=tree[deal].ch[1];
connect(ri,le,1);connect(le,0,1);
update(le);
}
dele(deal);
}
这个简单,如果我们找到了权值为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;
}
完整代码:
#include<iostream>
#include<cstdio>
using namespace std;
const int MAXN=1e6+10;
const int maxn=0x7fffff;
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 SPLAY
{
#define root tree[0].ch[1]
struct node
{
int v,fa,ch[2],rec,sum;
};
node tree[MAXN];
int pointnum,tot;
SPLAY(){pointnum=tot=0;}
int iden(int x){return tree[tree[x].fa].ch[0]==x?0:1;}
inline void connect(int x,int fa,int how){tree[x].fa=fa;tree[fa].ch[how]=x;}
inline void update(int x){tree[x].sum=tree[tree[x].ch[0]].sum+tree[tree[x].ch[1]].sum+tree[x].rec;}
inline void rotate(int x)
{
int y=tree[x].fa;
int R=tree[y].fa;
int Rson=iden(y);
int yson=iden(x);
int b=tree[x].ch[yson^1];
connect(b,y,yson);
connect(y,x,yson^1);
connect(x,R,Rson);
update(y);update(x);
}
void splay(int pos,int to)// 把编号为pos的节点旋转到编号为to的节点
{
to=tree[to].fa;
while(tree[pos].fa!=to)
{
if(tree[tree[pos].fa].fa==to) rotate(pos);
else if(iden(tree[pos].fa)==iden(pos)) rotate(tree[pos].fa),rotate(pos);
else rotate(pos),rotate(pos);
}
}
inline int newpoint(int v,int fa)//
{
tree[++tot].fa=fa;
tree[tot].v=v;
tree[tot].sum=tree[tot].rec=1;
return tot;
}
inline void dele(int x)
{
tree[x].ch[0]=tree[x].ch[1]=0;
if(x==tot) tot--;
}
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];
}
}
int build(int v)
{
pointnum++;
if(tot==0){root=1;newpoint(v,0);}
else
{
int now=root;
while(1)
{
tree[now].sum++;
if(tree[now].v==v){tree[now].rec++;return now;}//出现过
int nxt=v<tree[now].v?0:1;
if(!tree[now].ch[nxt])
{
newpoint(v,now);
tree[now].ch[nxt]=tot;
return tot;
}
now=tree[now].ch[nxt];
}
}
return 0;
}
inline void insert(int v)
{
int p=build(v);//p代表插到了哪里
splay(p,root);
}
void pop(int v)
{
int deal=find(v);
if(!deal) return ;
pointnum--;
if(tree[deal].rec>1){tree[deal].rec--;tree[deal].sum--;return ;}
if(!tree[deal].ch[0]) root=tree[deal].ch[1],tree[root].fa=0;
else
{
int le=tree[deal].ch[0];
while(tree[le].ch[1]) le=tree[le].ch[1];
splay(le,tree[deal].ch[0]);
int ri=tree[deal].ch[1];
connect(ri,le,1);connect(le,0,1);
update(le);
}
dele(deal);
}
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;
}
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;
}
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;
}
}s;
int main()
{
#ifdef WIN32
freopen("a.in","r",stdin);
#else
#endif
int n=read();
while(n--)
{
int opt=read(),x=read();
if(opt==1) s.insert(x);
else if(opt==2) s.pop(x);
else if(opt==3) printf("%d\n",s.rank(x));
else if(opt==4) printf("%d\n",s.arank(x));
else if(opt==5) printf("%d\n",s.lower(x));
else if(opt==6) printf("%d\n",s.upper(x));
}
}
至此,splay最常用的几种函数就解决了,
下面来看几道裸题
不知道为什么,我的splay跑的特别快,可能是脸太好了吧??
http://www.cnblogs.com/zwfymqz/p/7896128.html
标签:har ace ref 二叉查找树 org connect 定义 简单 roo
原文地址:http://www.cnblogs.com/zwfymqz/p/7898210.html