struct Tree{ //定义一个结构体来存树
int L,R; //L表示当前节点的左子节点,R表示当前节点的右子节点
int size; //size表示以当前节点为根节点的子树大小
}Treap[99];
inline void upt (int x){ //更新以x为根节点的子树大小
Treap[x].size=Treap[Treap[x].L].size+Treap[Treap[x].R].size+1;
return;
}
inline void Zig (int &x){ //右旋以x为根节点的子树
//用取地址符方便直接修改新的根节点
int z=Treap[x].L; //用z存储x的左子节点
Treap[x].L=Treap[z].R; //更新x左子节点为x左子节点的右子节点
Treap[z].R=x; //x原左子节点的右子节点更新为x
Treap[z].size=Treap[x].size; //维护子树大小
upt (x);
x=z; //当前子树根节点更新为z
return; //返回这颗新子树的根节点
}
inline void Zag (int &x){ //左旋以x为根节点的子树
//用取地址符方便直接修改新的根节点
int z=Treap[x].R; //用z存储x的右子节点
Treap[x].R=Treap[z].L; //更新x右子节点为x右子节点的左子节点
Treap[z].L=x; //x原右子节点的左子节点更新为x
Treap[z].size=Treap[x].size; //维护子树大小
upt (x);
x=z; //当前子树根节点更新为z
return; //返回这颗新子树的根节点
}
2-10
/ \
/ \
/ \
1-23 4-18
/ \
/ \
/ \
3-20 7-34
\
\
\
5
画风突然简陋
2-10
/ \
/ \
/ \
1-23 4-18
/ \
/ \
/ \
3-20 7-34
\
\
\
5-11
2-10
/ \
/ \
/ \
1-23 4-18
/ \
/ \
/ \
5-11 7-34
\
\
\
3-20
2-10
/ \
/ \
/ \
1-23 5-11
/ \
/ \
/ \
3-20 4-18
\
\
\
7-34
struct Tree{ //定义一个结构体来存树
int L,R; //L表示当前节点的左子节点,R表示当前节点的右子节点
int size; //size表示以当前节点为根节点的子树大小
int v; //v为当前节点的权值
int r; //r表示随机的优先级
}Treap[99];
inline void insert (int &x,int k){ //当前节点为x,插入一个权值为k的点
//用取地址符方便直接修改原值
if (!x){ //当前位置没有节点,即已经找到位置了
x=++Tot; //新建一个节点
Treap[x].v=k; //新节点的权值为k
Treap[x].r=rand (); //赋予一个随机的优先级
Treap[x].size=1; //以新节点为根节点的子树大小为1
return;
}
else ++Treap[x].size; //如果当前位置存在节点,那么新节点
//肯定在当前节点的左右子节点中
//所以直接将当前节点的子树大小加一
if (k<=Treap[x].v){ //如果k比当前节点权值小
insert (Treap[x].L,k);//向当前节点的左子节点继续搜索
if (Treap[x].r>Treap[Treap[x].L].r) //当优先级不满足小根堆时
Zig (x); //右旋当前节点
}
else { //如果k比当前节点权值大
insert (Treap[x].R,k); //向当前节点的右子节点继续搜索
if (Treap[x].r>Treap[Treap[x].R].r) //当优先级不满足小根堆时
Zag (x); //左旋当前节点
}
return;
}
2-10
/ \
/ \
/ \
1-23 5-11
/ \
/ \
/ \
3-20 4-18
\
\
\
7-34
2-10
/ \
/ \
/ \
1-23 5-11
/ \
/ \
/ \
3-20 4-18
2-10
/ \
/ \
/ \
1-23 5-11
/ \
/ \
/ \
3-20 7-34
2-10
/ \
/ \
/ \
1-23 4-18
/ \
/ \
/ \
5-11 7-34
/
/
/
3-20
2-10
/ \
/ \
/ \
1-23 4-18
/ \
/ \
/ \
3-20 7-34
struct Tree{ //定义一个结构体来存树
int L,R; //L表示当前节点的左子节点,R表示当前节点的右子节点
int size; //size表示以当前节点为根节点的子树大小
int v; //v为当前节点的权值
int r; //r表示随机的优先级
}Treap[99];
inline void Delete (int &x,int k){ //x为当前节点位置 k为需要删除的权值的节点
//用取地址符方便修改x父节点的子节点
if (Treap[x].v==k){ //找到需要删除的节点了
if (!Treap[x].L||!Treap[x].R) x=Treap[x].L+Treap[x].R; //第一种情况可以一起处理
//以为若该节点无子节点,x就会等于0
//若只有一个子节点,x就会变为他的存在的那个子节点
else if (Treap[Treap[x].L].r<Treap[Treap[x].R].r){ //当前节点左子节点的优先级比较小的情况
Zig (x); //右旋当前节点
Delete (x,k); //因为x被旋下去了所以继续向下找直到可以删除x这个节点
}
else { //当前节点左子节点的优先级比较大的情况
Zag (x); //左旋当前节点
Delete (x,k); //因为x被旋下去了所以继续向下找直到可以删除x这个节点
}
return;
}
Treap[x].size--; //维护子树大小,因为需要删除的节点一定在当前节点的子节点上
if (k<=Treap[x].v) //如果k比当前节点权值小
Delete (Treap[x].L,K); //向左寻找要删除节点
else //如果k比当前节点权值大
Delete (Treap[x].R,K); //向右寻找要删除节点
return;
}
inline int rank (int k){ //查询权值为k的排名
int ans=0,x=root; //ans为比k小的节点数 root为构造的Treap的根节点
while (x){ //当x不为0时即还可以找到节点
if (k==Treap[x].v) //找到节点了
return ans+Treap[Treap[x].L].size+1; //返回当前节点右子节点的子树大小加上
//之前比查询权值大的节点数加一
else if (k<Treap[x].v) //如果k比当前节点权值小
x=Treap[x].L; //向当前节点的左子节点进行查找,
//因为当前节点比查询权值大的话无法对答案做出任何贡献
else { //如果k比当前节点权值大
ans+=Treap[Treap[x].L].size+1; //比k小的节点数就增加了
//当前节点(1)和其左子树大小(Treap[Treap[x].L].size)
x=Treap[x].R //继续向左寻找
}
}
return 0; //如果不存在这个节点返回0(不一定为0,按照自己需求进行修改)
}
inline int rank (int k){ //查询排名为k的节点权值大小
int x=root; //root为构造的Treap的根节点
while (x){ //当x不为0时即还可以找到节点
if (k==Treap[Treap[x].L].size) //找到节点了
return Treap[x].v; //返回当前节点的权值
else if (k<=Treap[Treap[x].L].size) //如果k的排名比当前节点右子树大小小
x=Treap[x].L; //向当前节点的左子节点进行查找,
else { //如果k的排名比当前节点右子树大小大
k-=(Treap[Treap[x].L].size+1) //k要减去当前节点左子树大小+1
x=Treap[x].R //继续向左寻找
}
}
return 0; //如果不存在这个节点返回0(不一定为0,按照自己需求进行修改)
}
inline int QueryPre (int k){ //查询前驱
int x=root,ans=-2147483647; //x为根节点,ans为答案,初始化为极小值是为了防止没有找到前驱
while (x){ //循环至空的节点
if (Treap[x].v<=k){ //如果当前节点比k小
ans=Treap[x].v; //更新答案
x=Treap[x].R; //继续向左查找
}
else x=Treap[x].L; //如果当前节点比k大则不更新
//向左查找直到找到权值比k小的节点再更新
}
return ans; //返回答案
}
inline int QuerySuf (int k){ //查询后继,同上
int x=root,ans=2147483647;
while (x){
if (Treap[x].v>=k){
ans=Treap[x].v;
x=Treap[x].L;
}
else x=Treap[x].R;
}
return ans;
}
5
/ / / 3 7
/ \ / / \ / / \ / 1 4 6 9
5 7
/ \ / \ / \ 3 6 9
/ \
/ \
/ 1 4
struct node{ //用结构体存树
int L,R; //L,R分别为当前节点的左/右节点编号
int k; //k为当前节点权值
int r; //r为随机的优先级
int size; //size当前节点的子树大小
}treap[2020022];
inline void del (int x){ //调整子树大小
treap[x].size=treap[treap[x].R].size+treap[treap[x].L].size+1;
return;
}
inline void Split (int v,int k,int &L,int &R){ //v表示当前节点,k为分裂权值
//L表示可以添加到左树的位置
//R表示可以添加到右树的位置
//用取地址符来直接修改节点
if (!v){ //当当前节点为空节点时,左右树的可添加节点也变为空节点
L=R=0;
return;
}
if (treap[v].k<=k){ //当当前节点权值小于等于分裂权值k时
L=v; //将当前节点放到左树上可添加节点的位置
Split (treap[v].R,k,treap[v].R,R); //同时继续向右分裂,R不变
//但是左树可以更新的节点变为了当前节点的右子节点
//因为当前节点和其左子树都到了左树
//所以当前节点的右子节点空了出来= =
del (v); //调整子树大小,这个和Treap的函数是一样
return;
}
else if (treap[v].k>k){ //当当前节点权值大于分裂权值k时
R=v; //将当前节点放到右树上可添加节点的位置
Split (treap[v].L,k,L,treap[v].L); //同时继续向左分裂,L不变
//但是右树可以更新的节点变为了当前节点的左子节点
//理由同上
del (v); //调整子树大小
return;
}
}
Spilt (root,k,l,r); //按权值k将根节点为root的树分为两棵树
//左边树的根节点为l,右边树的根节点为r
struct node{ //用结构体存树
int L,R; //L,R分别为当前节点的左/右节点编号
int k; //k为当前节点权值
int r; //r为随机的优先级
int size; //size当前节点的子树大小
}treap[2020022];
inline int merge (int TA,int TB){ //合并根节点为TA和根节点为TB的树
if (!TA||!TB) return TA+TB; //当其中至少一个为空节点时返回那个非空节点
//若都为空节点直接返回空节点
if (treap[TA].r<treap[TB].r){ //比较根节点优先级大小,如果TA的优先级小时
treap[TA].R=merge (treap[TA].R,TB); //根节点为TA的右子节点更新为
//合并其右子树和根节点为TB的树 后的根节点
del (TA); //调整子树大小
return TA;
}
else{ //若TA优先级较大时
treap[TB].L=merge (TA,treap[TB].L); //根节点为TB的左子节点更新为
//合并根节点为TA的树和其左子树 后的根节点
del (TB); //调整子树大小
return TB;
}
}
5
/ / / 3 7
/ \ / / \ / / \ / 1 4 6 9
5 9
/ \
/ / \
3 7
/ \ /
/ \ /
/ \ /
1 4 6
5 9 8
/ \
/ / \
3 7
/ \ /
/ \ /
/ \ /
1 4 6
5 9
/ \
/ / \
3 7
/ \ / \
/ \ / \
/ \ / \
1 4 6 8
5
/ \
/ / \
3 7
/ \ / \
/ \ / \
/ \ / \
1 4 6 8
\
9
struct node{ //用结构体存树
int L,R; //L,R分别为当前节点的左/右节点编号
int k; //k为当前节点权值
int r; //r为随机的优先级
int size; //size当前节点的子树大小
}treap[2020022];
int root,l,r,Treap_size; //root为整棵平衡树的根节点编号
//l,r分别为分裂后的左右树根节点编号
//Treap_size为节点数
inline void newnode (int x){ //新建一个节点,节点数加一
treap[++Treap_size].size=1; //其子树大小为一
treap[Treap_size].k=x; //权值为插入节点权值
treap[Treap_size].r=rand(); //赋予随机的优先级
return;
}
inline void Insert (int x){ //插入权值为x的点
split (root,x,l,r); //按权值x分裂整棵树
newnode (x); //新建权值为x的节点
root=merge (l,Treap_size); //合并左树和新节点
root=merge (root,r); //合并上面合并后的树和右树
return;
}
5
/ \
/ / \
3 7
/ \ / \
/ \ / \
/ \ / \
1 4 6 8
\
9
5 8
/ \ / \ / \ 3 7 9
/ \ /
/ \ /
/ \ /
1 4 6
5 7 8
/ \ / \ / \ 3 6 9
/ \
/ \
/ \
1 4
inline void Delete (int x){ //删除权值为x的节点
int TC=0; //TC存三树的根节点
Split (root,x,l,r); //先按权值x分裂整棵树为一二树
//l为一树根节点编号,r为二树根节点编号
Split (l,x-1,l,TC); //然后按权值x-1分裂一树为一三树
//l为新的一树的根节点编号,TC为三树的根节点编号
TC=merge (treap[TC].L,treap[TC].R); //然后先将TC的左右子树合并,将TC节点删除
root=merge (l,TC); //合并一三树
root=merge (root,r); //合并剩下的两棵树
return;
}
inline int getrank (int k){ //查询权值为k的元素的排名
Split (root,k-1,l,r); //按权值为k-1分裂为两棵子树,那么左树的节点权值必定小于k
int res=treap[l].size+1; //答案就是左树的大小加一
root=merge (l,r); //不要忘记合并
return res;
}
inline int Kth (int v,int k){ //几乎一模一样的代码,我也不多做注释了
//只是注意v的意思是在以v为根节点的树中进行查找
while (v){
if (k<=treap[treap[v].L].size)
v=treap[v].L;
else if (k==treap[treap[v].L].size+1)
return treap[v].k;
else {
k-=(treap[treap[v].L].size+1);
v=treap[v].R;
}
}
return 0;
}
inline int QueryPre (int k){ //查询前驱
Split (root,k-1,l,r);
int res=Kth (l,treap[l].size);
root=merge (l,r);
return treap[res].k; //返回答案
}
inline int QuerySuf (int k){ //查询后继,同上
Split (root,k,l,r);
int res=Kth (r,1);
root=merge (l,r);
return treap[res].k; //返回答案
}
原文:https://www.cnblogs.com/IQZ-HUCSr-TOE/p/12630998.html