普通平衡树(模版)

在这里插入图片描述

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值