平衡树-有旋Treap
有旋Treap
可以在普通二叉搜索树的基础上保持优秀log复杂度
代码里有较为详细的注释
所以,话不多说,放代码qwq
#include<iostream>
using namespace std;
#define lid d[id].l
#define rid d[id].r
int cnt_tree,ans;
struct Treap{
int l,r; //左孩子,右孩子
int siz; //大小
int cnt; //重复元素数量
int rank; //随机出来的优先度
int val; //值
}d[100005];
void Update_size(int id){//更新大小
d[id].siz=d[id].cnt+d[lid].siz+d[rid].siz;
}
void lrotate(int &id){
/* 左旋:也就是让右子节点变成根节点
* A C
* / \ / \
* B C ----> A E
* / \ / \
* D E B D
*/
int t=d[id].r; //记录右孩子
d[id].r=d[t].l; //A的右孩子改为C的左孩子
d[t].l=id; //C的左孩子改为A
d[t].siz=d[id].siz;//传递size
Update_size(id);//更新A的size
id=t;//换根
}
void rrotate(int &id){
/* 右旋:也就是让左子节点变成根节点
* A C
* / \ / \
* B C <---- A E
* / \ / \
* D E B D
*/
int t=d[id].l;//同上
d[id].l=d[t].r;
d[t].r=id;
d[t].siz=d[id].siz;
Update_size(id);
id=t;
}
void insert(int &id,int val){
if(!id){ //没有点新建点
id=++cnt_tree;
d[id].rank=rand();
d[id].siz=1;
d[id].cnt=1;
d[id].val=val;
return;
}
d[id].siz++; //大小++
if(val==d[id].val){
d[id].cnt++; //值相同,直接扔进去
}
else if(val<d[id].val){
insert(lid,val); //增加在左孩子里
if(d[lid].rank<d[id].rank){
rrotate(id); //为满足小根堆性质(上方优先度低于下方),需要右旋
}
}
else{
insert(rid,val); //同上
if(d[rid].rank<d[id].rank){
lrotate(id);
}
}
}
//用bool,0为未删点,1为删点
bool del(int &id,int val){
if(!val) return false; //如果没有点,不需要修改,所以return false
if(val==d[id].val){ //如果相等
if(d[id].cnt>1){ //有重复元素则直接删除
d[id].cnt--;
d[id].siz--;
return true;
}
if(lid==0 || rid==0){ //只有一个孩子,或没有孩子,不存在内讧,故直接赋值
id=lid+rid;
return true;
}
else if(d[lid].rank<d[rid].rank){ //删完还要满足小根堆,故右旋
rrotate(id);
return del(id,val); //删点
}
else{
lrotate(id);
return del(id,val);
}
}
else if(val<d[id].val){
bool dele=del(lid,val); //点在左孩子,记录是否成功删点
if(dele) d[id].siz--;
return dele;
}
else{
bool dele=del(rid,val);
if(dele) d[id].siz--;
return dele;
}
}
int Query_Rank(int id,int val){ //查询排名
if(!id) return 0; //若 定义排名为比当前数小的数的个数+1 则此处应该为return 1;
if(val==d[id].val) return d[lid].siz+1; //仅比所有左侧节点大
else if(val<d[id].val) return Query_Rank(lid,val); //点在左孩子
else return d[lid].siz+d[id].cnt+Query_Rank(rid,val); //1.比所有左节点大,2.比该节点的所有重复元素大,3.点在右孩子
}
int Query_Num(int id,int val){ //查询排名为val的节点值
if(!id) return 0;
if(val<=d[lid].siz) return Query_Num(lid,val); //节点在左孩子
else if(val>d[lid].siz+d[id].cnt) return Query_Num(rid,val-d[lid].siz-d[id].cnt); //!节点在右孩子,但排名在右孩子里应减小
else return d[id].val; //不在左,不在右,就只能在自己里了呗awa
}
void Query_Pre(int id,int val){ //查询前驱
if(!id) return; //类似二分查找
if(val>d[id].val){ //!别弄反了
ans=id;
Query_Pre(rid,val);
}
else{
Query_Pre(lid,val);
}
}
void Query_Sub(int id,int val){
if(!id) return;
if(val<d[id].val){ //!别弄反了
ans=id;
Query_Sub(lid,val);
}
else{
Query_Sub(rid,val);
}
}
int main(){
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int n,root=0;
cin>>n;
for(int i=1;i<=n;i++){
int op,x;
cin>>op>>x;
if(op==1){
insert(root,x);
}
if(op==2){
del(root,x);
}
if(op==3){
cout<<Query_Rank(root,x)<<"\n";
}
if(op==4){
cout<<Query_Num(root,x)<<"\n";
}
if(op==5){
ans=0;
Query_Pre(root,x);
cout<<d[ans].val<<"\n";
}
if(op==6){
ans=0;
Query_Sub(root,x);
cout<<d[ans].val<<"\n";
}
}
}