#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<sstream>
#include<map>
#include<stack>
#include<queue>
#include<string>
#include<vector>
#include<algorithm>
#define ll long long
#define ull unsigned long long
#define mem(a) memset(a,0,sizeof(a))
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define sf(n) scanf("%d", &n)
#define inf 0x3f3f3f3f
#define endl '\n'
#define cyes cout<<"yes"<<endl
#define debug(x) cout<<"-----"<<x<<endl
using namespace std;
const int N = 1e5 + 5;
int n;
struct node
{
int l, r; //左右儿子
int key, val; //key是存的二叉树权值,val是堆的权重
int cnt, size; //cnt表示有多少个数, size表示一当前节点为根的子树的大小
} tr[N];
int root, idx;
void pushup(int id)
{
tr[id].size = tr[tr[id].l].size + tr[tr[id].r].size + tr[id].cnt;
}
int get_node(int key) //创建一个节点
{
tr[++idx].key = key;
tr[idx].val = rand(); //随机值
tr[idx].cnt = tr[idx].size = 1;
return idx;
}
void zig(int &p) //右旋
{
int q = tr[p].l; //p的左儿子
tr[p].l = tr[q].r, tr[q].r = p, p = q;
pushup(tr[p].r); pushup(p);
}
void zag(int &p) //左旋
{
int q = tr[p].r;
tr[p].r = tr[q].l, tr[q].l = p, p = q;
pushup(tr[p].l); pushup(p);
}
void build() //初始化
{
get_node(-inf); get_node(inf); //创建边界 ,哨兵
root = 1;tr[1].r = 2;
pushup(root);
if(tr[1].val < tr[2].val) zag(root);
}
void insert(int &p, int key) //插入一个值
{
if(!p) p = get_node(key); //为key创建一个节点
else if(tr[p].key == key) tr[p].cnt ++; //找到了和key相同的一个值
else if(tr[p].key > key) //插入p的左子树
{
insert(tr[p].l, key);
if(tr[tr[p].l].val > tr[p].val) zig(p);
}
else //插入p的右子树
{
insert(tr[p].r, key);
if(tr[tr[p].r].val > tr[p].val) zag(p);
}
pushup(p);
}
void remove(int &p, int key)
{
if(!p) return ; //要删除的数不存在,直接返回
if(tr[p].key == key) //找到了要删除的数
{
if(tr[p].cnt > 1) tr[p].cnt --; //个数大于1直接减掉一个
else if(tr[p].l || tr[p].r) //不是叶节点
{
if(!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val)
{
zig(p); //如果右子树不存在 或 左val大于右val 就右旋 ,向右删
remove(tr[p].r, key);
}
else
{
zag(p); //否则,左旋 , 向左删
remove(tr[p].l, key);
}
}
else p = 0; //是叶节点的话直接删除
}
else if(tr[p].key > key) remove(tr[p].l, key); //大于key,去左子树删
else remove(tr[p].r, key); //否则,去右子树删
pushup(p); //更新p
}
int get_rank_by_key(int p, int key) //由数值得到排名
{
if(!p) return 0; //节点不存在,(本题不会出现该情况)
if(tr[p].key == key) return tr[tr[p].l].size + 1; //左子树的大小 + 1
if(tr[p].key > key) return get_rank_by_key(tr[p].l, key); //在左子树就向左边找
return tr[tr[p].l].size + tr[p].cnt + get_rank_by_key(tr[p].r, key);
//否则向右边找, 注意要加上右子树的大小 以及 节点p的个数
}
int get_key_by_rank(int p, int rank) //由排名得到数值
{
if(!p) return inf; //节点不存在 ,(本题不会出现)
if(tr[tr[p].l].size >= rank) return get_key_by_rank(tr[p].l, rank); //在左边
if(tr[tr[p].l].size + tr[p].cnt >= rank) return tr[p].key; //一定就是当前节点p的值
return get_key_by_rank(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt); //在右边
//注意要减去左子树的个数 和 当前p节点的个数
}
int get_prev(int p, int key) //找到 key的前驱
{
if(!p) return -inf; //不存在
if(tr[p].key >= key) return get_prev(tr[p].l, key); //当前数和p的右子树肯定都大于key, 所以去左边找
return max(tr[p].key, get_prev(tr[p].r, key)); //当前数和右子树取一个最大
}
int get_next(int p, int key) //找到 key的后继
{
if(!p) return inf; // 不存在
if(tr[p].key <= key) return get_next(tr[p].r, key);
return min(tr[p].key, get_next(tr[p].l, key));
}
int main()
{
build();
scanf("%d", &n);
int op, x;
while(n--)
{
scanf("%d%d", &op, &x);
if(op == 1) insert(root, x);
else if(op == 2) remove(root, x);
else if(op == 3) printf("%d\n", get_rank_by_key(root, x) - 1); //由于有一个-inf的哨兵,所以实际排名要减去1
else if(op == 4) printf("%d\n", get_key_by_rank(root, x + 1)); //同样因为-inf的哨兵的存在, x要加1
else if(op == 5) printf("%d\n", get_prev(root, x));
else printf("%d\n", get_next(root, x));
}
return 0;
}
普通平衡树(模版)
最新推荐文章于 2024-04-01 15:31:51 发布