Treap学习笔记

Treap(树堆) 学习笔记(此处为带旋Treap)

Treap简介

Treap是一种二叉搜索树,其中,权值val满足二叉搜索树的性质,节点优先级priority满足堆的性质(作用后面会讲到)

Treap适用情况

因为属于二叉搜索树,所以可以维护二叉搜索树的信息,带旋Treap可以更好地控制树的深度,使得每次操作不至于被特殊数据卡成一条链使得单次操作复杂度从logn退化到n

Treap维护的信息

  1. 左右儿子的编号(带修,不适用堆式存储) lc rc
  2. 权值 w
  3. 子树大小 siz
  4. 节点个数 cnt (可能有重复权值的节点)
  5. 优先级 pos (维护堆的性质,用随机化给出)

Treap基本操作

(先给出维护siz的代码)void pushup(int &x){ siz(x)=siz(lc(x))+siz(rc(x))+cnt(x); return ; }

1.旋转

为了维护树的深度,把树进行一定旋转保证操作复杂度。
旋转分为左旋和右旋(图自oi-wiki):

那么我们不难写出旋转部分的代码

void zag(int &x){//左旋
	int y=rc(x);
	rc(x)=lc(y);
	lc(y)=x;
	siz(y)=siz(x);
	pushup(x);
	x=y;
	return ;
}
void zig(int &x){//右旋
	int y=lc(x);
	lc(x)=rc(y);
	rc(y)=x;
	siz(y)=siz(x);
	pushup(x);
	x=y;
	return ;
}

2.插入节点

  1. 没有这个节点时要创建一个新节点并编号初始化
  2. 有这个节点就cnt++
  3. 维护旋转:如果插入的是节点的左子树,且这个节点的左儿子的优先级小(大)于当前节点(取决于维护的是大根堆还是小根堆),那就右旋使得优先级小的节点深度减小,可以理解为向上移动,否则如果插入的右子树,同理。有如下代码
void Insert(int &x,int val){
	if(!x){
		x=++tot;c(x)=val;
		pos(x)=rand();//赋予随机优先级
		return ;
	}
	if(val<c(x)){//小于当前节点,插入当前节点的左子树
		Insert(lc(x),val);
		if(pos(lc(x))<pos(x)) zig(x);//右旋
	}
	else{
		Insert(rc(x),val);
		if(pos(rc(x))<pos(x)) zag(x);//左旋
	}
}

3.删除节点

主要是分类讨论:

  1. 节点有多个 cnt>1 只用cnt--就行了(维护siz)
    2.有左子树和右子树,考虑要让谁来替换这个节点
void Delete(int &x,int val){
	if(!x) return ;
	if(w(x)==val){
		if(cnt(x)>1){
			cnt(x)--;
			pushup(x);
			return ;
		}
		if(!lc(x)||!rc(x)) x=lc(x)+rc(x);
		else if(pos(lc(x))<pos(rc(x))) zig(x),Delete(x,val);//右旋使得左儿子成为父亲
		else zag(x),Delete(x,val);
		pushup(x);
		return ;
	}
	siz(x)--;
	if(val<w(x)) Delete(lc(x),val);
	else Delete(rc(x),val);
	pushup(x);
	return ;
}

4.查询某个值的排名

因为Treap是一棵二叉搜索树,满足二叉搜索树的性质,所以权值小的在左子树中,权值大的在右子树中,需要注意的是如果进入右子树查找,排名要加上左子树的大小(因为左子树的所有值都比右子树小)

int queryrank(int val){
	//查询val的排名	
	int x=root,p=0;
	while(x){
		if(w(x)==val) return p+siz(lc(x))+1;
		if(w(x)>val) x=lc(x);
		else p+=siz(lc(x))+cnt(x),x=rc(x);
	}
	return p;
}

5.查询排名为p的数

同上

int querykth(int p){
	//查询排名为p的数
	int x=root;
	while(x){
		if(siz(lc(x))<p && siz(lc(x))+cnt(x)>=p) 
			return w(x);
		if(siz(lc(x))>=p) x=lc(x);
		else p=p-siz(lc(x))-cnt(x),x=rc(x);
	}
	return 0;
}

6.查询x的前驱

从树根开始,一直向下找比x小的最大值

int query_pre(int val){
	//前趋,定义为小于x,且最大的数
	int x=root,pre=0;
	while(x){
		if(w(x)<val) pre=w(x),x=rc(x);
		else x=lc(x);
	}
	return pre;
}

从树根开始,一直向下找比x大的最小值

7.查询x的后驱

int query_nxt(int val){
	//后趋,定义为大于x,且最小的数
	int x=root,nxt=0;
	while(x){
		if(w(x)>val) nxt=w(x),x=lc(x);
		else x=rc(x);
	}
	return nxt;
}

完整代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+7;
int n,m,tot,root;
struct node{
	int lc,rc,fa,w,pos,siz,cnt;
	#define lc(x) tree[x].lc
	#define rc(x) tree[x].rc
	#define fa(x) tree[x].fa
	#define w(x) tree[x].w
	#define pos(x) tree[x].pos//随机优先级
	#define siz(x) tree[x].siz
	#define cnt(x) tree[x].cnt
}tree[N];
void pushup(int &x){
	siz(x)=siz(lc(x))+siz(rc(x))+cnt(x);
	return ;
}
void zag(int &x){//左旋
	int y=rc(x);
	rc(x)=lc(y);
	lc(y)=x;
	siz(y)=siz(x);
	pushup(x);
	x=y;
	return ;
}
void zig(int &x){//右旋
	int y=lc(x);
	lc(x)=rc(y);
	rc(y)=x;
	siz(y)=siz(x);
	pushup(x);
	x=y;
	return ;
}
void Insert(int &x,int val){
	if(!x){//空节点
		x=++tot;w(x)=val;siz(x)=cnt(x)=1;
		lc(x)=rc(x)=0;pos(x)=rand();//赋予随机优先级
		return ;
	}
	++siz(x);
	if(w(x)==val){
		++cnt(x);
		pushup(x);
		return ;
	}
	if(val<w(x)){//小于当前节点,插入当前节点的左子树
		Insert(lc(x),val);
		if(pos(lc(x))<pos(x)) zig(x);
	}
	else{
		Insert(rc(x),val);
		if(pos(rc(x))<pos(x)) zag(x);
	}
	pushup(x);
	return ;
}
void Delete(int &x,int val){
	if(!x) return ;
	if(w(x)==val){
		if(cnt(x)>1){
			cnt(x)--;
			pushup(x);
			return ;
		}
		if(!lc(x)||!rc(x)) x=lc(x)+rc(x);
		else if(pos(lc(x))<pos(rc(x))) zig(x),Delete(x,val);
		else zag(x),Delete(x,val);
		pushup(x);
		return ;
	}
	siz(x)--;
	if(val<w(x)) Delete(lc(x),val);
	else Delete(rc(x),val);
	pushup(x);
	return ;
}
int query_pre(int val){
	//前趋,定义为小于x,且最大的数
	int x=root,pre=0;
	while(x){
		if(w(x)<val) pre=w(x),x=rc(x);
		else x=lc(x);
	}
	return pre;
}
int query_nxt(int val){
	//后趋,定义为大于x,且最小的数
	int x=root,nxt=0;
	while(x){
		if(w(x)>val) nxt=w(x),x=lc(x);
		else x=rc(x);
	}
	return nxt;
}
int queryrank(int val){
	//查询val的排名	
	int x=root,p=0;
	while(x){
		if(w(x)==val) return p+siz(lc(x))+1;
		if(w(x)>val) x=lc(x);
		else p+=siz(lc(x))+cnt(x),x=rc(x);
	}
	return p;
}
int querykth(int p){
	//查询排名为p的数
	int x=root;
	while(x){
		if(siz(lc(x))<p && siz(lc(x))+cnt(x)>=p) 
			return w(x);
		if(siz(lc(x))>=p) x=lc(x);
		else p=p-siz(lc(x))-cnt(x),x=rc(x);
	}
	return 0;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	srand(time(0));
	cin>>n;
	int op,x;
	while(n--){
		cin>>op>>x;
		if(op==1) Insert(root,x);
		if(op==2) Delete(root,x);
		if(op==3){//只要这里注意原树中可能没有这个节点,先插入再查询,最后删除 
			Insert(root,x);
			cout<<queryrank(x)<<'\n';	
			Delete(root,x);
		}
		if(op==4) cout<<querykth(x)<<'\n';
		if(op==5) cout<<query_pre(x)<<'\n';
		if(op==6) cout<<query_nxt(x)<<'\n';
	}	
	return 0;
}
posted @   爱艺诗篇  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
点击右上角即可分享
微信分享提示