【学习笔记】数据结构:无旋Treap

最近学习了一下无旋Treap,发现无旋Treap真的太好打了,而且也很好理解,很好用。

Treap?

什么是Treap?
顾名思义:Treap=Tree+heap,即“树堆”,“树”指的是二叉查找树,“堆”就是堆。
那二叉查找树跟堆有什么关系呢?
当输入的数据十分恶心的时候,普通的二叉查找树的时间复杂度就会由 n l o g 2 n nlog_2n nlog2n 退化到 n 2 n^2 n2 级别,时间复杂度难以让人接受。
而我们现在考虑优化,我们可以在每一个节点上多放一个数,然后使得这棵树既满足二叉排序树的性质也满足堆的性质,那么就可以尽可能地避免极端数据的影响。
有的人要问了,那这个数怎么求啊?
很简单,随机数。但C++的rand()函数太慢,因此我们也可以手打,毕竟也不怕题人卡。

如何维护?

一般的Treap是通过旋转来保持这个性质,但是这样打常数很大并且代码量也很大,不大好打。
于是我们便引入了无旋Treap。

无旋Treap

顾名思义,不用旋转的Treap叫做无旋Treap。
那我们怎样才能维护呢?
先说明指针是什么意思:

struct Treap{
   
	int ls,rs;//左右儿子
	int id;//随机数
	int val;//该节点的值
	int size;//该节点的子树大小
}T[1000005];

那就要引入Treap的两个核心操作:split(分裂)merge(合并)
这两个操作到底有啥用呢,待会儿就知道了。

Split

分裂一般来说有两种方法:

  1. 按照数值分成两颗Treap,一颗上面的值全部小于等于(或大于等于)val,另一颗相反。
  2. 按照size来分裂

一般来说是采取第一种方法,在此也重点将这一种方法。
那我们怎么分裂呢?
当我们遇到了一个节点,我们判断,假若这个节点的val<=需要分裂的val,那么我们把这个节点以及它的左子树放到a里面,然后往右递归。否则相反。
是不是很简单?

//更新
void update(int p){
   
	T[p].size=T[T[p].ls].size+T[T[p].rs].size+1;
	return;
}
//分裂
void split(int p,int &x,int &y,int val){
   
	if (p==0){
   
		x=y=0;
		return;
	}
	if (T[p].val<=val){
   
		x=p;
		split(T[p].rs,T[x].rs,y,val);
	}
	else{
   
		y=p;
		split(T[p].ls,x,T[y].ls,val);
	}
	update(p
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值