————————————————
版权声明:本文为CSDN博主「ModestCoder_」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ModestCoder_/article/details/90139481

清空一个节点
确定一个节点是父亲的左儿子还是右儿子
更新一个节点
把一个点连到另一点下面
上旋
splay
插入一个点
查询一个数的排名
查询排名为k的数
前驱、后继
删除一个点

#include <bits/stdc++.h>
#define maxn 100010
using namespace std;
int sz, rt, f[maxn], key[maxn], size[maxn], recy[maxn], son[maxn][]; inline int read(){
int s = , w = ;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -;
for (; isdigit(c); c = getchar()) s = (s << ) + (s << ) + (c ^ );
return s * w;
} void clear(int x){
f[x] = key[x] = size[x] = recy[x] = son[x][] = son[x][] = ;
//5个数组全部清零
} int get(int x){
return son[f[x]][] == x;
//如果自己是父亲的右儿子,返回1;否则返回0(0为左儿子,1为右儿子)
} void update(int x){
if (x){//如果这个点存在
size[x] = recy[x];//自己重复的次数先累计
if (son[x][]) size[x] += size[son[x][]];
if (son[x][]) size[x] += size[son[x][]];
//如果存在儿子,把儿子的size累积到自己
//然后发现一个问题,更新自己的size时,必须保证儿子的size是正确的
//所以后面的操作,当牵扯到儿子和父亲时,应该先更新新儿子,后更新新父亲
}
} void connect(int x, int y, int z){//x连到y下面,关系为z
if (x) f[x] = y;//存在x,则x的父亲为y
if (y) son[y][z] = x;//存在y,y的z关系儿子为x
} void rotate(int x){//上旋x
int fa = f[x], ffa = f[fa], m = get(x), n = get(fa);//确定x,fa的关系
connect(son[x][m ^ ], fa, m);//把要转的儿子转到父亲下,关系为m
connect(fa, x, m ^ );//把父亲转到自己下面,关系为m^1
connect(x, ffa, n);//把自己转到父亲的父亲下,关系为n
update(fa), update(x);//先更新fa,再更新自己,可以自己想想为什么是这个顺序
} void splay(int x){
for (int fa; fa = f[x]; rotate(x))//每次总是旋转自己
if (f[fa]) rotate(get(x) == get(fa) ? fa : x);//如果有爷爷(父亲的父亲),看父亲与父亲的父亲的关系决定转哪个
rt = x;//别忘了,把根赋为当前点
} void insert(int x){
if (!rt){//树中没有一个节点
rt = ++sz;
key[rt] = x;
size[rt] = recy[rt] = ;
son[rt][] = son[rt][] = ;//赋初值
return;
}
int now = rt, fa = ;
while (){
if (key[now] == x){//树中已有此点,重复+1
++recy[now];
update(now); update(fa);
splay(now);//splay一下,保证平衡
return;
}
fa = now, now = son[now][x > key[now]];//满足二叉查找树的性质,往下跑
if (!now){
++sz;
key[sz] = x;
size[sz] = recy[sz] = ;//赋初值
f[sz] = fa;//父亲是fa
son[fa][x > key[fa]] = sz;//更新父亲的新儿子
update(fa);//更新父亲的size
splay(sz);//splay一下,保证平衡
return;
}
}
} int find(int x){//查排名
int now = rt, ans = ;
while (){
if (x < key[now]){
now = son[now][]; continue;//在左子树中
}
ans += size[son[now][]];//排名加上左子树节点个数
if (x == key[now]){ splay(now); return ans + ; }//值等于当前点,splay一下,保证平衡,排名+1为当前排名
ans += recy[now];//排名加上当前节点的数的个数
now = son[now][];//在右子树中
}
} int kth(int x){//查找排名为x的数
int now = rt;
while (){
if (son[now][] && x <= size[son[now][]]){//在左子树中
now = son[now][]; continue;
}
if (son[now][]) x -= size[son[now][]];//存在左儿子,排名减去左子树节点数
if (x <= recy[now]){ splay(now); return key[now]; }//说明就是当前点,splay一下,保证平衡,退出
x -= recy[now];//排名减去当前节点数的个数
now = son[now][];//在右子树中
}
} int pre(){//前驱为左子树中最大的那个
int now = son[rt][];
while (son[now][]) now = son[now][];
return now;
} int nxt(){//后继为右子树中最小的那个
int now = son[rt][];
while (son[now][]) now = son[now][];
return now;
} void del(int x){
int no_use = find(x);//find主要是把当前数的对应点找到,然后旋到根,返回值的排名在这里没用
if (recy[rt] > ){//情况1:有重复,重复-1,更新,退出
--recy[rt];
update(rt);
return;
}
//接下来都是没有重复的情况
if (!son[rt][] && !son[rt][]){//情况2:没有儿子,直接清空
clear(rt);
rt = ;
return;
}
if (!son[rt][]){//情况3:没有左儿子,只有右儿子,右儿子变成根,清除自己
int tmp = rt;
f[rt = son[rt][]] = ;
clear(tmp);
return;
}
if (!son[rt][]){//情况4:没有右儿子,只有左儿子,左儿子变成根,清除自己
int tmp = rt;
f[rt = son[rt][]] = ;
clear(tmp);
return;
}
//情况5:两个儿子都有,这是需要一个很简便的做法
//把前驱splay到根,保持左子树其他节点不用动
//原根右儿子变成前驱的右儿子
//原根功成身退,清除掉
//最后对前驱的size进行更新
int tmp = rt, left = pre();
splay(left);
connect(son[tmp][], rt, );
clear(tmp);
update(rt);
} int main(){
int M = read();
while (M--){
int opt = read(), x = read();
if (opt == ) insert(x);
if (opt == ) del(x);
if (opt == ) printf("%d\n", find(x));
if (opt == ) printf("%d\n", kth(x));
if (opt == ){
insert(x); printf("%d\n", key[pre()]); del(x);
}
if (opt == ){
insert(x); printf("%d\n", key[nxt()]); del(x);
}
}
return ;
}

最新文章

  1. 在Microsoft-IIS/10.0上面部署mvc站点的时候,出现404的错误
  2. oracle常用的SQL语句
  3. C++Builder 解决绘图闪动问题
  4. (21)odoo中的QWeb模板引擎
  5. 获取Spring-boot系统环境变量方法
  6. VIM的高级使用
  7. 转载:iPhone 6 Plus 屏幕宽度问题 375 vs 414
  8. 【WPF】路由事件
  9. HOOK windows消息 C# 代码
  10. Android 实现ListView异步加载图片
  11. ffprobe使用具体解释
  12. WPF 启动初始界面
  13. QiQi and Symmerty
  14. gem 安装nokigiri
  15. sql server多行数据(一列)转换成一个字段
  16. poj 1639 Picnic Planning 度限制mst
  17. linux-----docker
  18. Dockerfile封装Django镜像
  19. Django 数据库连接配置(Oracle、Mysql)
  20. swift 要点

热门文章

  1. sql 映射文件
  2. Java反射机制——学习总结
  3. Android教程2020 - RecyclerView显示多种item
  4. 使用H5与webGL的3D 可视化地铁展示
  5. C++ 函数详解
  6. 基于HTTPS的接口测试——nginx配置SSL
  7. mplayer的参数
  8. Idea使用插件实现逆向工程搭建SpringBoot项目
  9. Codeforces_794
  10. 使用Apache服务器实现Nginx反向代理