Treap模板

本文分享了作者在实现洛谷题目P3369中使用平衡树(如AVL树或红黑树)的经验,详细讲解了插入、删除和查询操作的代码实现,以及如何保持树的平衡性。通过实例演示了如何维护节点优先级和权值计数,适用于数据结构学习者进阶阅读。
摘要由CSDN通过智能技术生成

以前写线段树的时候就天天叫代码多,今天写了平衡树,呵呵。。。

洛谷P3369普通平衡树

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
const int N=1e6,inf=0x3f3f3f3f;
struct node
{
	int r;//随机的优先级
	int size,val,cnt;//val为节点的权值,cnt为相同权值节点合并后的个数
	int ch[2];//ch[0]为左儿子,ch[1]为右儿子 
}tree[N];
int n,rt,tot;
void rotate(int &u,int d)//d=0为左旋,d=1为右旋 
{
	int v=tree[u].ch[!d];
	//调整父子关系 
	tree[u].ch[!d]=tree[v].ch[d];
	tree[v].ch[d]=u;
	//更新节点信息
	//u现在为v的儿子,必须先更新u的信息,才能正确更新v的信息
	tree[u].size=tree[tree[u].ch[0]].size+tree[tree[u].ch[1]].size+tree[u].cnt;
	tree[v].size=tree[tree[v].ch[0]].size+tree[tree[v].ch[1]].size+tree[v].cnt;
	//返回v的编号,因为当前访问的已经是v了 
	u=v;
}
void insert(int &k,int x)
{
	if(k==0)//如果当前节点为空,就新建节点 
	{
		k=++tot;
		tree[k].r=rand();
		tree[k].val=x;
		tree[k].cnt=tree[k].size=1;
		return;
	}
	if(tree[k].val==x) {tree[k].cnt++,tree[k].size++;return;}//如果已有权值相等的节点,个数+1 
	int d=x<tree[k].val?0:1;//小于就进入左子树,大于则进入右子树 
	insert(tree[k].ch[d],x);
	if(tree[tree[k].ch[d]].r<tree[k].r) rotate(k,!d);
	//如果不满足小根堆性质,将u的儿子节点v旋上来 
	
	//旋转前,k中的值为u(注意k储存的无论为何值,其意义都为当前函数正在访问的节点的编号;
	//而k中的某个具体的值u只表示treap树中的任一节点) 
	//如果u这个节点被旋下去,则当前函数访问的节点就变成了v,则k会被赋值为旋上来的节点v 
	
	//当函数结束后,k中的值会因为取地址符&而返回至上一个函数的ch[d]中
	//这样来实现对于原本u的父亲(即原本v的爷爷)的儿子指针的更改 
	tree[k].size=tree[tree[k].ch[0]].size+tree[tree[k].ch[1]].size+tree[k].cnt;//更新节点信息 
}
void remove(int &k,int x)
{
	if(k==0) return;//如果当前节点为空,返回 
	if(tree[k].val==x)
	{
		if(tree[k].cnt==1)//如果该权值的节点只有一个
		{
			if(tree[k].ch[0]==0||tree[k].ch[1]==0)//如果没有儿子,或只有一个儿子 
			k=tree[k].ch[tree[k].ch[0]?0:1];//将儿子提上来 
			else//否则将左右儿子中随机优先级小的节点提上来 
			{
				int d=tree[tree[k].ch[0]].r<tree[tree[k].ch[1]].r?0:1;
				rotate(k,!d);
				remove(tree[k].ch[!d],x);//继续向下递归 
			}
		}
		else tree[k].cnt--;//否则个数减1 
	}
	else remove(tree[k].ch[x<tree[k].val?0:1],x);//否则继续递归 
	tree[k].size=tree[tree[k].ch[0]].size+tree[tree[k].ch[1]].size+tree[k].cnt;//更新节点信息 
}
int get_rank(int k,int x)
{
	if(x==tree[k].val) return tree[tree[k].ch[0]].size+1;//如果当前节点就是x 
	if(x<tree[k].val) return get_rank(tree[k].ch[0],x);//如果x在左子树中 
	//如果x在右子树中,要加上左子树大小和当前节点的个数 
	if(x>tree[k].val) return tree[tree[k].ch[0]].size+tree[k].cnt+get_rank(tree[k].ch[1],x);
}
int get_val(int k,int x)
{
	if(x<=tree[tree[k].ch[0]].size) return get_val(tree[k].ch[0],x);//如果在左子树中 
	else if(x<=tree[tree[k].ch[0]].size+tree[k].cnt) return tree[k].val;//如果cnt个当前节点中排名包括x
	//如果在右子树中,查找右子树中排名为(x-左子树大小-当前节点个数)的节点 
	else return get_val(tree[k].ch[1],x-tree[tree[k].ch[0]].size-tree[k].cnt);
}
int get_pre(int k,int x)
{
	if(k==0) return -inf;//如果节点为空返回负无穷,避免干扰其他答案 
	//如果当前节点的值大于等于x,向左子树寻找更小的值 
	if(x<=tree[k].val) return get_pre(tree[k].ch[0],x);
	//如果当前节点的值小于x,向右子树寻找最大的值 
	if(tree[k].val<x) return max(tree[k].val,get_pre(tree[k].ch[1],x));
}
int get_nxt(int k,int x)
{
	if(k==0) return inf;//如果节点为空返回正无穷,避免干扰其他答案 
	//如果当前节点的值小于等于x,向右子树寻找更大的值 
	if(tree[k].val<=x) return get_nxt(tree[k].ch[1],x);
	//如果当前节点的值大于x,向左子树寻找最小的值 
	if(x<tree[k].val) return min(tree[k].val,get_nxt(tree[k].ch[0],x));
}
int main()
{
	scanf("%d",&n);
	for(int i=1,op,x;i<=n;i++)
	{
		scanf("%d%d",&op,&x);
		if(op==1) insert(rt,x);
		if(op==2) remove(rt,x);
		if(op==3) printf("%d\n",get_rank(rt,x));
		if(op==4) printf("%d\n",get_val(rt,x));
		if(op==5) printf("%d\n",get_pre(rt,x));
		if(op==6) printf("%d\n",get_nxt(rt,x));
	}
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值