首页 > 其他分享 >Treap 学习笔记

Treap 学习笔记

时间:2023-05-02 09:02:10浏览次数:45  
标签:return int 复杂度 笔记 旋转 学习 Treap inline 节点

一、Treap

Treap 是一种通过旋转操作维护性质的二叉搜索树。

定义详见

要维护的东西还是一样,对于每个节点,要维护它的左右儿子,子树大小,还有权值和随机的优先级(这样才能保证树的高度是 \(O(\log n)\) 级别的)。

注意:旋转、分裂、伸展什么的都是手段,维持平衡树的 2 个性质才是目的。

对于全局,要维护树根的编号和当前有多少个节点。

二、实现

1. 旋转

由于插入、删除操作需要维护二叉搜索树的性质,所以我们需要一个操作来调整 Treap。它的核心操作就是旋转。

旋转的目标是在整棵树的中序遍历不变的情况下改变父子关系,让优先级小的节点更浅。而中序遍历递增可以在插入、删除操作中维护。

image

我们惊喜地看到,全树中序遍历没有变(都是 4 的子树->2->5 的子树->1->3 的子树),并且有且仅有 1、2 父子关系改变了。

然后来讲一下旋转操作具体怎么做。先放代码。

1. 维护操作

维护一个节点的子树大小,就是它自己加上左右子树的大小。

void upd(int x){
	t[x].s=t[t[x].l].s+t[t[x].r].s+1;
}

时间复杂度 \(O(1)\)。

2. 右旋

inline void rrot(int &x){
	int y=t[x].l;
	t[x].l=t[y].r;
	t[y].r=x;
	upd(x);
	upd(y);
	x=y;
}

大概就是这样(绿色的节点表示维护完成):

  1. 记录 \(y\) 为 \(x\) 的左子节点。

image

  1. 令 \(x\) 的左节点变为 \(y\) 的右节点。

image

  1. 令 \(y\) 的右节点变为 \(x\)。

image

  1. 维护 \(x\),再维护 \(y\)(顺序不能乱)

image

  1. 令根节点为 \(y\)。

image

时间复杂度 \(O(1)\)。

3. 左旋

我们发现,右旋和左旋互为逆运算,而且左右对称,所以我们把右旋的所有左右反过来就行啦。

inline void lrot(int &x){
	int y=t[x].r;
	t[x].r=t[y].l;
	t[y].l=x;
	upd(x);
	upd(y);
	x=y;
}

时间复杂度 \(O(1)\)。

4. 插入

要插入一个数,而且要保证二叉搜索树性质,所以我们递归:

  1. 判断要插入的数与当前节点的关系。如果小于等于,插入到左子树。否则插入到右子树。

  2. 如果遇到一个空节点,就新增一个节点并返回。

  3. 然后回溯时要维护堆性质。那么我们往哪个方向插入了新数字,那个方向的堆性质才会被破坏。所以判断一下那个方向的堆性质有没有被破坏,如果有,进行对应的旋转即可。

inline void ins(int &x,int v){
	if(!x){
		t[x=++c]={0,0,1,v,rand()};
		return;
	}
	t[x].s++;
	if(v<=t[x].v){
		ins(t[x].l,v);
		if(t[t[x].l].p<t[x].p)rrot(x);
	}else{
		ins(t[x].r,v);
		if(t[t[x].r].p<t[x].p)lrot(x);
	}
}

时间复杂度 \(O(树高)\),也就是 \(O(\log n)\)。当然实际上跑不满,因为一旦回溯到某一个地方时,发现不用旋转也满足了堆性质,那么这个地方及以上都不用旋转了。

5. 删除

要删除一个数,采用二叉堆的思想,将一个数旋转到叶子节点再删除。由于旋转操作不改变二叉搜索树性质,所以我们要维护堆性质:一个数的优先级小于等于它的儿子。那我们在将一个数向下旋转的时候,肯定是选择一个优先级小的转上来。

inline void del(int &x,int v){
	if(t[x].v==v){
		if(!t[x].l||!t[x].r){
			x=t[x].l+t[x].r;
			return;
		}
		if(t[t[x].l].p>t[t[x].r].p){
			lrot(x);
			del(t[x].l,v);
		}else{
			rrot(x);
			del(t[x].r,v);
		}
	}else if(t[x].v>v)del(t[x].l,v);
	else del(t[x].r,v);
	upd(x);
}

时间复杂度 \(O(\log n)\)。

6. 查询 x 数的排名

照样是分左右子树查询。

注意一定要查询到空节点为止。

inline int vtr(int x,int v){
	if(!x)return 1;
	if(t[x].v>=v)return vtr(t[x].l,v);
	return vtr(t[x].r,v)+t[t[x].l].s+1;
}

时间复杂度 \(O(\log n)\)。

7. 查询排名为 x 的数

inline int rtv(int x,int v){
	if(t[t[x].l].s==v-1)return t[x].v;
	if(t[t[x].l].s>=v)return rtv(t[x].l,v);
	return rtv(t[x].r,v-1-t[t[x].l].s);
}

时间复杂度 \(O(\log n)\)。

8. 查询前驱

inline int pre(int x,int v){
	if(!x)return -I;
	if(t[x].v>=v)return pre(t[x].l,v);
	return max(t[x].v,pre(t[x].r,v));
}

时间复杂度 \(O(\log n)\)。

9. 查询后缀

inline int nxt(int x,int v){
	if(!x)return I;
	if(t[x].v<=v)return nxt(t[x].r,v);
	return min(t[x].v,nxt(t[x].l,v));
}

时间复杂度 \(O(\log n)\)。

标签:return,int,复杂度,笔记,旋转,学习,Treap,inline,节点
From: https://www.cnblogs.com/qwq-qaq-tat/p/17367225.html

相关文章

  • Quixel Mixer学习笔记:软件入门使用
    本随笔用于记录随笔作者在学习使用纹理和材质制作软件QuixelMixer时学到的知识点,属于入门级别的笔记。本随笔使用的QuixelMixer版本为2022.1.1Beta,内容整理自官方手册。随笔作者还处在学习阶段,在软件的使用和理解还不够透彻,难免在技术上或书写上出现问题,如出现类似的问题欢迎......
  • Mastering Regular Expressions(精通正则表达式) 阅读笔记:第一章,概念
    RealScenario(现实场景)Here'sthescenario:you'regiventhejobofcheckingthepagesonawebserverfordoubledwords(suchas"thisthis"),acommonproblemwithdocumentssubjecttoheavyediting.任务:检查文本中重复的单词(doubledwords),比如&q......
  • 韦东山Linux快速入门笔记
    Linux操作基础1.git下载文档:在一个文件夹中右键点击GitBashhere,打开一个终端窗口:在窗口中输入:gitclonehttps://e.coding.net/weidongshan/01_all_series_quickstart.git 另外,可以用图中gitpullorigin拉取更新  2.$PATH有三种修改办法3.删除文件夹一......
  • 李宏毅transformer笔记
     首先这里解决的问题是Seq2Seq列出各种场景,语音识别,机器翻译,chatbot 当前现在NLP模型之所以这么重要,在于他的通用能力,很多场景都可以转换成Seq2Seqsummary,情感分析啊,只要你能通过QA和机器交互的场景都可以是Seq2Seq这里的例子,语法树解析,多元分类,甚至是对象识别Seq2Seq......
  • Vue3 新特性 笔记整理
    一.基于Vite的构建vite优点(可以快速构建vue项目比webpack打包更加快捷)1.快速的冷启动2.及时的模块热更新3.真正的按需编译举例:vite3构建vue3项目npminitvite=>选择框架,选择类别npminstall安装依赖 注:vite构建后的项目,不包含路由等脚手架,需要按需导入 二......
  • MySQL学习笔记:基于GTID的主从复制
    GTID的主从复制背景GTID出现之前,在一主多从的复制拓扑中,如果主库宕机,需要从多个从库选择之一作为新主库,这个过程比较复杂。没有一种直接了当的方法找到其它从库对应的新主库二进制日志坐标。通常的做法是先要寻找每个从库复制原主库的最后语句,然后找到新主库中包含该语句的二进制......
  • 代码笔记27 numpy和pytorch中的多维数组切片
    原来还可以用数组切数组,我算是长见识了。不多说了,直接上代码应该可以明白importnumpyasnpxyz=np.arange(36).reshape(3,4,3)B,N,C=xyz.shapefarthest=np.random.randint(0,N,size=B)#torch.randint(0,N,(B,),dtype=torch.long)#初始时随机选择一点(B......
  • Irwin-Hall 分布学习笔记
    定理:Irwin-Hall分布对于\(n\)个在\([0,1]\)内均匀分布的实数随机变量,它们的和不超过一个实数\(z\)的概率为:\[F(z)=\sum\limits_{k=0}^{\lfloorz\rfloor}(-1)^k\binom{n}{k}\frac{(z-k)^n}{n!}\]证明:首先明确一个概念:概率密度。对于一个随机变量\(X\),在\([0,1]\)......
  • 2023 qbxt 笔记整理
    洛谷P4460n<20,试试状压设\(dp[i][j]\)表示状态为i,最后一个点为j(当前在点j)。枚举当前点为i,要转移的点为k转移:$dp[i|(1<<k-1)][k]+=dp[i][j]$还需要判断一下三点连线在不在同一条直线上。代码:#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;inl......
  • 2022年Web前端开发流程和学习路线(详尽版)
    本文的最新内容,更新于2022-06-27,会在GitHub上同步更新,欢迎star。大家完全不用担心这篇文章会过时,因为随着前端领域的技术更新,本文也会随之更新。前言前端侧重于人机交互和用户体验,后端侧重于业务逻辑和大规模数据处理。理论上,面向用户的产品里,所有问题(包括产品、设计、后端......