#include
#include
#include
using namespace std;
//treap 就是tree+heap 利用了二叉堆的结构整体趋于平衡树(不一定是严格意义上的平衡)
//又利用了二叉平衡树的排序 才让查找 插入的效率在logn
struct data{//该树的标号为完全二叉树标号方式
int l,r,v,size,rnd,w;//分别表示左右子树的序号,节点的值,
//以该节点为根左右子树加上该点总共有多少值,节点的随机值(是建成二叉堆的依据),等于v的值有几个(因为可能有好几个相同的值)
}tr[100005];
int n,size,root,ans;
void update(int k)//更新结点信息
{
tr[k].size=tr[tr[k].l].size+tr[tr[k].r].size+tr[k].w;
}
void rturn(int &k)//参考左旋
{
int t=tr[k].l;tr[k].l=tr[t].r;tr[t].r=k;
tr[t].size=tr[k].size;update(k);k=t;
}
void lturn(int &k)//左旋
{
int t=tr[k].r;
tr[k].r=tr[t].l;//该点的右子树变成原来右子树的左子树
tr[t].l=k;//原来的右子树的左子树为该点
tr[t].size=tr[k].size;//原来右子树的大小就是为该点值的大小(好好考虑下 因为旋转了这棵树总的大小还是不变的)
update(k);//更新该点信息
k=t;//因为左旋后位置发生了改变 原来右子树的点到了该节点的位置 所以k赋值为t
}
void insert(int &k,int x)//使用引用可直接赋值
{ //插入肯定会最后被安排到叶子节点
if(k==0)//若已经到了叶子节点
{
size++;k=size;//总节点数加一 给该节点标号
tr[k].size=tr[k].w=1;//该点的大小和w值都赋值为1
tr[k].v=x;
tr[k].rnd=rand();//产生随机数并赋值
return;
}
tr[k].size++;//插入一个数 所查找经过的值都加一 因为从上往下走 一定是它的子孙会被插入添加 所以该节点也要修改
if(tr[k].v==x)tr[k].w++;//插入的数已经存在且就是该树 则直接在w上加一
else if(x>tr[k].v)//如果大于该节点值
{
insert(tr[k].r,x);//向右寻找要插入的点
if(tr[tr[k].r].rnd1)//要删的值有好几个
{
tr[k].w--;tr[k].size--;return;//直接把计数减少
}
if(tr[k].l*tr[k].r==0)k=tr[k].l+tr[k].r;//如果该节点是只有一个子节点或者没有子节点 该节点直接被子节点替代
else if(tr[tr[k].l].rnd tr[k].v)//查找 该路径上的值都要减一
tr[k].size--,del(tr[k].r,x);
else tr[k].size--,del(tr[k].l,x);
}
int query_rank(int k,int x)//查询x的排名 就是把小于自己的树(左子树)的综合加上自身(加1)
{
if(k==0)return 0;
if(tr[k].v==x)return tr[tr[k].l].size+1;//找到了该节点 把左子树的个数加上自身的个数(只加一 因为只要求最小的那个值)
else if(x>tr[k].v)//如果大于该数将此节点的左子树和本身节点的w加起来再去右子树中找小于该数的数
return tr[tr[k].l].size+tr[k].w+query_rank(tr[k].r,x);
else return query_rank(tr[k].l,x);//如果x小于当前节点从左子树找
}
int query_num(int k,int x)//查询排名为x的数
{
if(k==0)return 0;
if(x<=tr[tr[k].l].size)//如果x比左子树的总和小就往左子树里找
return query_num(tr[k].l,x);
else if(x>tr[tr[k].l].size+tr[k].w)// 如果比该节点的左子树和该点w的总和大往右子树找第x-tr[tr[k].l].size-tr[k].w个值
return query_num(tr[k].r,x-tr[tr[k].l].size-tr[k].w);
else return tr[k].v;//刚好缩到为1的时候的那个值就是查的那个值
}
void query_pro(int k,int x)//查找该x的前驱 小于x的最大值
{
if(k==0)return;
if(tr[k].vx)
{
ans=k;query_sub(tr[k].l,x);
}
else query_sub(tr[k].r,x);
}
int main()
{
scanf("%d",&n);
int opt,x;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&opt,&x);
switch(opt)
{
case 1:insert(root,x);break; //root=1
case 2:del(root,x);break;
case 3:printf("%d\n",query_rank(root,x));break;
case 4:printf("%d\n",query_num(root,x));break;
case 5:ans=0;query_pro(root,x);printf("%d\n",tr[ans].v);break;
case 6:ans=0;query_sub(root,x);printf("%d\n",tr[ans].v);break;
}
}
return 0;
}
参考:
https://www.cnblogs.com/MyStringIsNotNull/p/9165675.html 这里有很多图可以参考 特别是左右旋
https://baike.baidu.com/item/Treap/4321536?fr=aladdin