Treap树堆:一种排序二叉树(中序遍历权值有序)
每个节点有两个关键字:key[]和rand[]
其中key[]满足二叉搜索树性质,rand[]满足堆性质(即Tree+Heap=Treap)即
如果vl是u的左孩子,则key[vl]<key[u],rand[vl]<rand[u]
如果vr是u的右孩子,则key[vr]>key[ur],rand[vr]<rand[u],但rand[vr]和rand[vl]的大小关系不确定
每次插入节点x时先按key[]值插入满足二叉搜索树的性质,插入之后,随机生成该节点rand[]值,之后按rand[]值大小调整树的结构(翻转),以满足堆性质,期望树高(logn)
每次删除时将要删除的节点翻转到叶子然后删除(或者翻转到它只有一个儿子的情况)
翻转规则如下:
没了
3224: Tyvj 1728 普通平衡树
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 14281 Solved: 6197
[ Submit][ Status][ Discuss]
Description
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
Input
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
Output
对于操作3,4,5,6每行输出一个数,表示对应答案
Sample Input
10
1 106465
4 1
1 317721
1 460929
1 644985
1 84185
1 89851
6 81968
1 492737
5 493598
Sample Output
106465
84185
492737
#include<stdio.h>
#include<stdlib.h>
using namespace std;
typedef struct
{
int l, r;
int val, rnd;
int size, sum;
}Tree;
Tree tre[100005];
int n, id, root, ans;
void Update(int k)
{
tre[k].size = tre[tre[k].l].size+tre[tre[k].r].size+tre[k].sum;
}
void Rtroot(int &k)
{
int t;
t = tre[k].l;
tre[k].l = tre[t].r;
tre[t].r = k;
tre[t].size = tre[k].size;
Update(k);
k = t;
}
void Ltroot(int &k)
{
int t;
t = tre[k].r;
tre[k].r = tre[t].l;
tre[t].l = k;
tre[t].size = tre[k].size;
Update(k);
k = t;
}
void Insert(int &k, int x)
{
if(k==0)
{
k = ++id;
tre[k].size = tre[k].sum = 1;
tre[k].val = x;
tre[k].rnd = rand();
return;
}
tre[k].size++;
if(tre[k].val==x)
tre[k].sum++; //如果要插入的权值在树中已有节点,直接更新数量
else if(x>tre[k].val)
{
Insert(tre[k].r, x);
if(tre[tre[k].r].rnd<tre[k].rnd)
Ltroot(k); //维护堆性质
}
else
{
Insert(tre[k].l, x);
if(tre[tre[k].l].rnd<tre[k].rnd)
Rtroot(k);
}
}
void Delete(int &k, int x)
{
if(k==0)
return;
if(tre[k].val==x)
{
if(tre[k].sum>1)
{
tre[k].sum--;
tre[k].size--;
return;
}
//↓删除节点k
if(tre[k].l*tre[k].r==0)
k = tre[k].l+tre[k].r;
else if(tre[tre[k].l].rnd<tre[tre[k].r].rnd) //将k点通过翻转往叶子方向移动,左旋还是右旋看哪种满足堆性质
{
Rtroot(k);
Delete(k, x);
}
else
{
Ltroot(k);
Delete(k, x);
}
}
else if(x>tre[k].val)
{
tre[k].size--;
Delete(tre[k].r, x);
}
else
{
tre[k].size--;
Delete(tre[k].l, x);
}
}
int Query_rank(int k, int x)
{
if(k==0)
return 0;
if(tre[k].val==x)
return tre[tre[k].l].size+1;
else if(x>tre[k].val)
return tre[tre[k].l].size+tre[k].sum+Query_rank(tre[k].r,x);
else
return Query_rank(tre[k].l, x);
}
int Query_num(int k, int x)
{
if(k==0)
return 0;
if(x<=tre[tre[k].l].size)
return Query_num(tre[k].l, x);
else if(x>tre[tre[k].l].size+tre[k].sum)
return Query_num(tre[k].r, x-tre[tre[k].l].size-tre[k].sum);
else
return tre[k].val;
}
void Query_pro(int k, int x)
{
if(k==0)
return;
if(tre[k].val<x)
{
ans = k;
Query_pro(tre[k].r, x);
}
else
Query_pro(tre[k].l, x);
}
void Query_sub(int k, int x)
{
if(k==0)
return;
if(tre[k].val>x)
{
ans = k;
Query_sub(tre[k].l, x);
}
else
Query_sub(tre[k].r, x);
}
int main(void)
{
int t, x, i;
scanf("%d", &n);
id = root = 0;
for(i=1;i<=n;i++)
{
scanf("%d%d", &t, &x);
switch(t)
{
case 1: Insert(root, x); break;
case 2: Delete(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", tre[ans].val); break;
case 6: ans=0; Query_sub(root, x); printf("%d\n", tre[ans].val); break;
}
}
return 0;
}