放在前面的话

本蒟蒻因为最近的题目总是搞点奇奇怪怪的平衡树,就去学了下\(Treap\)

现在来总结一下

由于本人是个蒟蒻,本文可能有部分错误,麻烦各位读者大佬在评论区提醒

什么是\(Treap\)

\(Treap\)取自两个单词,一是\(tree\),一是\(heap\)

也就是说,\(Treap\)结合了二叉搜索树和堆

\(Treap\)维持平衡的方法

方法就是

随 机 数!!!

不要不信,真的是随机数

对于每个点,\(Treap\)给予它们一个随机数

并要求在满足二叉搜索树的基础上,随机数要形成一个大(小)根堆


接下来将给出一道模板题,\(Treap\)的操作将在模板题的讲解中给出

例题讲解

放例题

\(Treap\)例题

讲解

数组

\(size[i]\)表示以\(i\)为根的子树的大小

\(num[i]\)表示值为\(i\)的个数

\(val[i]\)表示节点\(i\)的值

\(son[i][0/1]\)表示\(i\)的左/右儿子

\(rd[i]\)表示节点\(i\)的随机值

这些数组在接下来会多次提及,各位读者大佬可以稍稍记忆一下

操作

统计(\(pushup\))

重新统计以\(i\)为根的子树的大小

当前大小:左儿子的大小+右儿子的大小+当前这个数的个数

void pushup(int p)
{
size[p]=size[son[p][0]]+size[son[p][1]]+num[p];
}

旋转(\(rot\))!!!

\(Treap\)的核心操作

分为左旋和右旋,但是思路一样,故一起介绍

旋转的目的是将一个子节点移到父亲处,在过程中满足二叉搜索树的性质

以右旋为例

一开始是这样的



现在我们要将\(B\)转到\(A\)那里

怎么搞呢???

根据二叉搜索树的性质我们知道

\(B<A\)

那么可以把\(A\)丢给\(B\)当右儿子

但是\(B\)已经有了右儿子\(y\)了

再想,根据二叉搜索树有\(B<y<A\)

那么\(y\)就可以丢给\(A\)当左儿子

然后\(B\)的左儿子和\(A\)的右儿子不变

旋转完了之后



检查一下大小关系

旋转前:\(x<B<y<A<z\)

旋转后:\(x<B<y<A<z\)

一模一样

具体操作呢

上代码

void rot(int &p,int d)
{
int k=son[p][d^1];
son[p][d^1]=son[k][d];
son[k][d]=p;
pushup(p);
pushup(k);
p=k;
}

\(d\)为0是左旋,为1右旋

以\(d=1\)为例(右旋)

     a
/ \
b z
/ \
x y

\(k\)为\(p\)的左儿子

先把\(k\)的右儿子丢给\(p\)当左儿子:

son[p][d^1]=son[k][d];

现在长这样

              a(p)
/ \
b(k) y z
/
x

再把\(p\)丢给\(k\)当右儿子

son[k][d]=p;

变成了这样

     b(k)
/ \
x a(p)
/ \
y z

再\(pushup(p和k)\)

最后\(p=k\)

结束

那么我们就可以用旋转来维护堆了

插入(\(ins\))

要插入一个数\(x\)

可以一直判断\(x\)与当前节点的大小关系,选择往哪边递归

直到找到一棵空子树就把\(x\)放进去

放进去之后看一下\(rd\)值的大小,有不对的就旋转

插入后重新统计大小

void ins(int &p,int x)
{
if (!p)
{
sum++;
p=sum;
size[p]=num[p]=1;
val[p]=x;
rd[p]=rand();
return;
}
if (val[p]==x)
{
num[p]++;
size[p]++;
return;
}
int d=(x>val[p]);
ins(son[p][d],x);
if (rd[p]<rd[son[p][d]]) rot(p,d^1);
pushup(p);
}

删除(\(del\))

跟插入差不多

\(x<val[p]\)往左边走

\(x>val[p]\)往右边走

有点不同的是在\(x=val[p]\)的时候

分4种情况讨论

  1. 无左儿子和右儿子
  2. 无左儿子
  3. 无右儿子
  4. 有左儿子和右儿子

情况1:删自己

情况2:左旋,往左边走

情况3:右旋,往右边走

情况4:看哪边的\(rd\)值大,就旋转哪边,往那边走

删除完之后重新统计一下大小

void del(int &p,int x)
{
if (!p) return;
if (x<val[p]) del(son[p][0],x);
else if (x>val[p]) del(son[p][1],x);
else
{
if (!son[p][0]&&!son[p][1])
{
num[p]--;
size[p]--;
if (!num[p]) p=0;
}
else if (!son[p][1])
{
rot(p,1);
del(son[p][1],x);
}
else if (!son[p][0])
{
rot(p,0);
del(son[p][0],x);
}
else
{
int d=(rd[son[p][0]]>rd[son[p][1]]);
rot(p,d);
del(son[p][d],x);
}
}
pushup(p);
}

查询排名(\(get\_rank\))

如果没有这个数,返回0

如果\(val[p]=x\),输出左儿子的大小+1

如果\(val[p]>x\),往左儿子走

如果\(val[p]<x\),往右儿子走,并输出左儿子的大小+\(num[x]\)+\(x\)在右儿子中的排名

int get_rank(int p,int x)
{
if (!p) return 0;
if (val[p]==x) return (size[son[p][0]]+1);
if (val[p]<x) return (size[son[p][0]]+num[p]+get_rank(son[p][1],x));
if (val[p]>x) return get_rank(son[p][0],x);
}

查询值(\(get\_sum\))

如果\(size[son[p][0]>x\) 往左边走

如果\(size[son[p][0]+num[p]<x\) 往右边走,在右边查找排名\(x-size[son[p][0]-num[p]\)的数

若都不满足,返回\(val[p]\)

int get_sum(int p,int x)
{
if (!p) return 0;
if (size[son[p][0]]>=x) return get_sum(son[p][0],x);
else if (size[son[p][0]]+num[p]<x) return get_sum(son[p][1],x-size[son[p][0]]-num[p]);
else return val[p];
}

查询前驱(\(get\_pre\))

如果当前\(p\)为0返回\(-∞\)(一定要特别小)

如果\(val[p]>=x\),即在左儿子中,那就往左边走

否则的话返回当前值和右儿子中的前驱里大的那个(所以为什么要特别小)

int get_pre(int p,int x)
{
if (!p) return -inf;
if (val[p]>=x) return get_pre(son[p][0],x);
else return max(val[p],get_pre(son[p][1],x));
}

查询后继(\(get\_suc\))

跟查询前驱类似

只不过为0的时候返回\(∞\),因为后面是\(min\)

左儿子和右儿子换一下就可以

int get_suc(int p,int x)
{
if (!p) return inf;
if (val[p]<=x) return get_suc(son[p][1],x);
else return min(val[p],get_suc(son[p][0],x));
}

到此所有的操作都已讲解完毕

Code——总

#include<cstdio>
#include<stdlib.h>
#include<iostream>
#define inf 2147483647
using namespace std;
int n,pd,x,s,sum,size[100005],son[100005][3],val[100005],num[1000005],rd[100005];
void pushup(int p)
{
size[p]=size[son[p][0]]+size[son[p][1]]+num[p];
}
void rot(int &p,int d)
{
int k=son[p][d^1];
son[p][d^1]=son[k][d];
son[k][d]=p;
pushup(p);
pushup(k);
p=k;
}
void ins(int &p,int x)
{
if (!p)
{
sum++;
p=sum;
size[p]=num[p]=1;
val[p]=x;
rd[p]=rand();
return;
}
if (val[p]==x)
{
num[p]++;
size[p]++;
return;
}
int d=(x>val[p]);
ins(son[p][d],x);
if (rd[p]<rd[son[p][d]]) rot(p,d^1);
pushup(p);
}
void del(int &p,int x)
{
if (!p) return;
if (x<val[p]) del(son[p][0],x);
else if (x>val[p]) del(son[p][1],x);
else
{
if (!son[p][0]&&!son[p][1])
{
num[p]--;
size[p]--;
if (!num[p]) p=0;
}
else if (!son[p][1])
{
rot(p,1);
del(son[p][1],x);
}
else if (!son[p][0])
{
rot(p,0);
del(son[p][0],x);
}
else
{
int d=(rd[son[p][0]]>rd[son[p][1]]);
rot(p,d);
del(son[p][d],x);
}
}
pushup(p);
}
int get_rank(int p,int x)
{
if (!p) return 0;
if (val[p]==x) return (size[son[p][0]]+1);
if (val[p]<x) return (size[son[p][0]]+num[p]+get_rank(son[p][1],x));
if (val[p]>x) return get_rank(son[p][0],x);
}
int get_sum(int p,int x)
{
if (!p) return 0;
if (size[son[p][0]]>=x) return get_sum(son[p][0],x);
else if (size[son[p][0]]+num[p]<x) return get_sum(son[p][1],x-size[son[p][0]]-num[p]);
else return val[p];
}
int get_pre(int p,int x)
{
if (!p) return -inf;
if (val[p]>=x) return get_pre(son[p][0],x);
else return max(val[p],get_pre(son[p][1],x));
}
int get_suc(int p,int x)
{
if (!p) return inf;
if (val[p]<=x) return get_suc(son[p][1],x);
else return min(val[p],get_suc(son[p][0],x));
}
int main()
{
freopen("104.in","r",stdin);
scanf("%d",&n);
while (n--)
{
scanf("%d%d",&pd,&x);
if (pd==1) ins(s,x);
if (pd==2) del(s,x);
if (pd==3) printf("%d\n",get_rank(s,x));
if (pd==4) printf("%d\n",get_sum(s,x));
if (pd==5) printf("%d\n",get_pre(s,x));
if (pd==6) printf("%d\n",get_suc(s,x));
}
return 0;
}

教学之Treap的更多相关文章

  1. Yeoman 官网教学案例:使用 Yeoman 构建 WebApp

    STEP 1:设置开发环境 与yeoman的所有交互都是通过命令行.Mac系统使用terminal.app,Linux系统使用shell,windows系统可以使用cmder/PowerShell/c ...

  2. Linux实战教学笔记08:Linux 文件的属性(上半部分)

    第八节 Linux 文件的属性(上半部分) 标签(空格分隔):Linux实战教学笔记 第1章 Linux中的文件 1.1 文件属性概述(ls -lhi) linux里一切皆文件 Linux系统中的文件 ...

  3. Linux实战教学笔记07:Linux系统目录结构介绍

    第七节 Linux系统目录结构介绍 标签(空格分隔):Linux实战教学笔记 第1章 前言 windows目录结构 C:\windows D:\Program Files E:\你懂的\精品 F:\你 ...

  4. Linux实战教学笔记06:Linux系统基础优化

    第六节 Linux系统基础优化 标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 基础环境 第2章 使用网易163镜像做yum源 默认国外的yum源速度很慢,所以换成国内的. 第一步:先备份 ...

  5. Linux实战教学笔记05:远程SSH连接服务与基本排错(新手扫盲篇)

    第五节 远程SSH连接服务与基本排错 标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 远程连接LInux系统管理 1.1 为什么要远程连接Linux系统 在实际的工作场景中,虚拟机界面或物理 ...

  6. Linux实战教学笔记04:Linux命令基础

    第四节:Linux命令基础 标签(空格分隔):Linux实战教学笔记 第1章 认识操作环境 root:当前登陆的用户名 @分隔符 chensiqi:主机名 -:当前路径位置 用户的提示符 1.1 Li ...

  7. Linux实战教学笔记03:操作系统发展历程及系统版本选择

    标签(空格分隔): Linux实战教学笔记-陈思齐 第1章 Linux简介 1.1 什么是操作系统? 简单讲:操作系统就是一个人与计算机硬件的中介. 操作系统,英文名称Operating System ...

  8. Linux实战教学笔记02:计算机系统硬件核心知识

    标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 互联网企业常见服务器介绍 1.1 互联网公司服务器品牌 - DELL(大多数公司,常用) - HP - IBM(百度在用) 浪潮 联想 航天联 ...

  9. Linux实战教学笔记01:计算机硬件组成与基本原理

    标签(空格分隔): Linux实战教学笔记 第1章 如何学习Linux 要想学好任何一门学问,不仅要眼睛看,耳朵听,还要动手记,勤思考,多交流甚至尝试着去教会别人. 第2章 服务器 2.1 运维的基本 ...

随机推荐

  1. [C#] (原创)一步一步教你自定义控件——04,ProgressBar(进度条)

    一.前言 技术没有先进与落后,只有合适与不合适. 本篇的自定义控件是:进度条(ProgressBar). 进度条的实现方式多种多样,主流的方式有:使用多张图片去实现.使用1个或2个Panel放到Use ...

  2. .netcore中的依赖注入

    IOC.DI相关概念的理解 1.依赖:简单的讲就是"引用到".例如AccountController.cs引用到IAccountService.cs,那么AccountContro ...

  3. Python UnboundLocalError: local variable 'xxx' referenced before assignment 解决方法

    一.报错含义: val=9 def test(): print(val) val = 6 print(val) test() 翻译:本地变量xxx引用前没有定义. 二.报错原因 这是Python变量作 ...

  4. 【SpringBoot】11-1.Springboot整合Springmvc+Mybatis增删改查操作(下)

    整合过程:https://www.isdxh.com/68.html 一.增--增加用户 1.创建实体类 package com.dxh.pojo; public class Users { priv ...

  5. 微信小程序获取普通二维码

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  6. SQL2005数据库可疑的解决方法

    sqlserver数据库标注为可疑的解决办法 一般引起可疑的原因是突然断电,服务器死机,强制关机导致正在运行的数据库文件损坏,需要进行修复.方法一:USE MASTER GOSP_CONFIGURE ...

  7. 记载idea创建spring-boot项目时“Spring Initalizr Error”的问题处理

  8. 手撸ORM浅谈ORM框架之Query篇

    快速传送 手撸ORM浅谈ORM框架之基础篇 手撸ORM浅谈ORM框架之Add篇 手撸ORM浅谈ORM框架之Update篇 手撸ORM浅谈ORM框架之Delete篇 手撸ORM浅谈ORM框架之Query ...

  9. 八位“Booth二位乘算法”乘法器

    目录 八位"Booth二位乘算法"乘法器 原理 补码乘法器 Booth一位乘 Booth二位乘 设计思路 减法变加法 vivado特性 设计文件 综合电路 测试文件 仿真波形 八位 ...

  10. 深入浅出 webpack 之基础配置篇

    前言 前端工程化经历过很多优秀的工具,例如 Grunt.Gulp.webpack.rollup 等等,每种工具都有自己适用的场景,而现今应用最为广泛的当属 webpack 打包了,因此学习好 webp ...