[学习笔记] fhq Treap 平衡树

fhq Treap 也叫无旋Treap (好像?我也不知道)

反正我带旋 Treap 是不会滴,其他的平衡树也不会(但是会平板电视)

fhq Treap 好写,码量小,缺点是常数比较大

定义

二叉搜索树

二叉搜索树是一种二叉树的树形数据结构,其定义如下:

  1. 空树是二叉搜索树。

  2. 若二叉搜索树的左子树不为空,则其左子树上所有点的附加权值均小于其根节点的值。

  3. 若二叉搜索树的右子树不为空,则其右子树上所有点的附加权值均大于其根节点的值。

  4. 二叉搜索树的左右子树均为二叉搜索树。

至于二叉搜索树怎么写我也不知道

但是由于可以构造数据使得二叉搜索树退化成一条链所以平衡树就应运而生了

平衡树是通过左旋和右旋各种奇怪的操作使左子树和右子树的高度最多相差 1 的二叉搜索树

Treap 就是一种弱平衡的平衡树

Treap 顾名思义就是 Tree + Heap 是加入了堆来防止二叉搜索树退化(说白了就是随机化

其中,二叉搜索树的性质是:

左子节点的值( val \textit{val} val)比父节点大
右子节点的值( val \textit{val} val)比父节点小(当然这也是可以反过来的)

堆的性质是:

子节点值( key \textit{key} key)比父节点大或小(取决于是小根堆还是大根堆)
不难看出,如果用的是同一个值,那这两种数据结构的性质是矛盾的,所以我们再在搜索树的基础上,引入一个给堆的值 key \textit{key} key。对于 val \textit{val} val 值,我们维护搜索树的性质,对于 key \textit{key} key 值,我们维护堆的性质。其中 key \textit{key} key 这个值是随机给出的。

搬个 OI-Wiki 的图片
在这里插入图片描述
Treap 的核心操作是左旋和右旋 而 fhq Treap 则是不带旋的 Treap,很多情况下会一些操作好写很多

fhq Treap

fhq Treap 的核心操作是分裂 ( s p l i t split split) 和 合并 ( m e r g e merge merge)

节点信息

一个节点中的信息应该很好想罢,值 v a l val val ,键值 k e y key key, 左儿子 l l l ,右儿子 r r r 以及子树大小 s i z siz siz

struct treap{int val,key,siz,l,r;
}fhq[N << 1];

建立新节点

建立一个新节点其实就是把一个节点初始化掉

int new_treap(int val){fhq[++cnt].val = val;fhq[cnt].key = rand();fhq[cnt].siz = 1;return cnt;
}

很好理解对吧

更新父节点信息

其实就和线段树的 p u s h _ u p push \_ up push_up操作是一样的

void push_up(int pos){fhq[pos].siz = fhq[fhq[pos].l].siz + fhq[fhq[pos].r].siz + 1;
}

分裂

分裂操作有两种,一种是按值分裂,把所有值小于等于 v a l val val 的分裂成一颗树,把值 大于 v a l val val分裂成一颗树;一种是按大小分裂,把小于等于给定大小的分裂成一棵树,大于给定大小的分裂成一颗树

一般把 fhq Treap 当正常平衡树使用的时候都是用按值分裂

直接看代码理解罢

void split(int pos, int val, int& x, int& y){//因为是一棵树分裂成两颗树,返回pair会比较麻烦,所以直接引用一下 if(!pos) {//到底了不能分裂 x = y = 0;return;}if(fhq[pos].val <= val){//小于val的分裂到x树去 x = pos;split(fhq[pos].r, val, fhq[pos].r, y);//右子树继续分裂 }else{y = pos;//大于val到y树去 split(fhq[pos].l,val,x,fhq[pos].l);//左子树继续分裂 }push_up(pos);
}

合并

合并当然就是分裂反过来噜~~

int merge(int x, int y){//注意这里x树是分裂出来小的那个树 if(!x || !y) return x+y;//如果x为空就返回y,如果y没有就返回x,如果两个都没有就返回0 if(fhq[x].key > fhq[y].key){//x的键值大,y并到x的右子树去 fhq[x].r = merge(fhq[x].r, y);push_up(x);return x;}else{fhq[y].l = merge(x,fhq[y].l);//y的键值大,x并到y的左子树去 push_up(y);return y;}
}

平衡树常规操作

前面两个操作就很短对吧,后面还要短

插入一个值 val

插入分两步

  1. 把树按 v a l val val 分裂成两棵树
  2. v a l val val 和小于等于 v a l val val 那棵树合并,再合并回去
void insert(int val){int x,y;split(root,val,x,y);root = merge(merge(x,new_treap(val)),y);
}

删除一个值 val

删除分为三步

  1. v a l val val 分裂成两棵树
  2. 把小于等于 v a l val val 的那棵树再按 v a l − 1 val-1 val1 分裂成两棵树(这样我们就得到了全是 v a l val val 的一棵树)
  3. 把全是 v a l val val 的那棵树的根节点删掉(合并左子树和右子树就可以了)
  4. 把剩下的树按顺序合并回去
void del(int val){int x,y,z;split(root,val,x,z);split(x,val-1,x,y);y = merge(fhq[y].l,fhq[y].r);root = merge(merge(x,y),z);
}

查询 val 的排名

这个也很简单啊,就两步

  1. 把树按 v a l − 1 val-1 val1 分裂
  2. 小的那棵树的 s i z + 1 siz+1 siz+1 就可以了
  3. 记得合并回去
void get_rank(int val){int x,y;split(root,val-1,x,y);cout << fhq[x].siz+1 << endl;root = merge(x,y);
}

查询排名为 rank 的值

这个稍微麻烦一点

void get_num(int rank){int now = root;while(now){if(fhq[fhq[now].l].siz+1 == rank) break;else if(fhq[fhq[now].l].siz >= rank) now = fhq[now].l;else{rank -= fhq[fhq[now].l].siz+1;now = fhq[now].r;}}cout << fhq[now].val << endl;
}

查找前驱/后继

void pre(int val){int x,y;split(root,val-1,x,y);int now = x;while(fhq[now].r) now = fhq[now].r;cout << fhq[now].val << endl;root = merge(x,y);
}
void nxt(int val){int x,y;split(root,val,x,y);int now = y;while(fhq[now].l){now = fhq[now].l;}cout << fhq[now].val << endl;root = merge(x,y);
}

板子传送门

Code

#include <bits/stdc++.h>
const int N = 1e5+10;
using namespace std;
struct treap{int val,key,siz,l,r;
}fhq[N << 1];
int cnt = 0,root;
int new_treap(int val){fhq[++cnt].val = val;fhq[cnt].key = rand();fhq[cnt].siz = 1;return cnt;
}
void push_up(int pos){fhq[pos].siz = fhq[fhq[pos].l].siz + fhq[fhq[pos].r].siz + 1;
}
void split(int pos, int val, int& x, int& y){//因为是一棵树分裂成两颗树,返回pair会比较麻烦,所以直接引用一下 if(!pos) {//到底了不能分裂 x = y = 0;return;}if(fhq[pos].val <= val){//小于val的分裂到x树去 x = pos;split(fhq[pos].r, val, fhq[pos].r, y);//右子树继续分裂 }else{y = pos;//大于val到y树去 split(fhq[pos].l,val,x,fhq[pos].l);//左子树继续分裂 }push_up(pos);
}
int merge(int x, int y){//注意这里x树是分裂出来小的那个树 if(!x || !y) return x+y;//如果x为空就返回y,如果y没有就返回x,如果两个都没有就返回0 if(fhq[x].key > fhq[y].key){//x的键值大,y并到x的右子树去 fhq[x].r = merge(fhq[x].r, y);push_up(x);return x;}else{fhq[y].l = merge(x,fhq[y].l);//y的键值大,x并到y的左子树去 push_up(y);return y;}
}
void insert(int val){int x,y;split(root,val,x,y);root = merge(merge(x,new_treap(val)),y);
}
void del(int val){int x,y,z;split(root,val,x,z);split(x,val-1,x,y);y = merge(fhq[y].l,fhq[y].r);root = merge(merge(x,y),z);
}
void get_rank(int val){int x,y;split(root,val-1,x,y);cout << fhq[x].siz+1 << endl;root = merge(x,y);
}
void get_num(int rank){int now = root;while(now){if(fhq[fhq[now].l].siz+1 == rank) break;else if(fhq[fhq[now].l].siz >= rank) now = fhq[now].l;else{rank -= fhq[fhq[now].l].siz+1;now = fhq[now].r;}}cout << fhq[now].val << endl;
}
void pre(int val){int x,y;split(root,val-1,x,y);int now = x;while(fhq[now].r) now = fhq[now].r;cout << fhq[now].val << endl;root = merge(x,y);
}
void nxt(int val){int x,y;split(root,val,x,y);int now = y;while(fhq[now].l){now = fhq[now].l;}cout << fhq[now].val << endl;root = merge(x,y);
}
int n;int main(){cin >> n;while(n--){int op,x;cin >> op >> x;if(op == 1){insert(x);}if(op == 2){del(x);}if(op == 3){get_rank(x);}if(op == 4){get_num(x);}if(op == 5){pre(x);}if(op == 6){nxt(x);}}	return 0;
}

感觉只实现这些不够?那可以去看看下一篇fhq 实现文艺平衡树

如果有任何不足之处或者疑问,欢迎在评论区提出

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/67176.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode】409. 最长回文串

409. 最长回文串&#xff08;简单&#xff09; 方法&#xff1a;哈希表 贪心 思路 不难发现&#xff0c;回文字符串一定是由 若干偶数个字符 至多一个奇数个字符 组成 。我们可以使用一个长度为 128 的 hash表来记录每一个字符的出现次数&#xff0c;当该字符出现了两次&am…

pycharm 下jupyter noteobook显示黑白图片不正常

背景现象&#xff1a; 1、显示一张黑白图片&#xff0c;颜色反过来了。 from IPython.display import display source Image.open(examples/images/forest_pruned.bmp) display(source) 2、原因&#xff1a; 是pycharm会在深色皮肤下默认反转jupyter notebook输出图片的颜…

面试如何回答弹性盒子布局这个问题呢?

在我们面试中如果被问道css方面的面试题 那么极有可能被问到的一道面试题就是弹性盒子&#xff0c;本篇文章通过一张图带你拿捏这道面试题。 1、首先需要说一说弹性盒子的基本概念&#xff1a;弹性盒子是一种用于网页布局中创建灵活和响应式设计的CSS布局模型。 2、其次需要说…

note_前端框架Vue的安装和简单入门(Windows 11)

1. Vue安装 (1) 下载安装node.js和npm # 下载msi安装包 https://nodejs.org/en# 点击安装包&#xff0c;按提示安装 # 默认安装nodejs, npm, 在线文档; PATH配置# 确认安装是否成功&#xff0c;在dos中输入 node -v # 验证nodejs是否安装成功 npm -v # 验证nodejs包管…

Python常用IDE选择与安装

1、IDE简介 选择一款高效而又顺手的IDE学习或使用Python&#xff0c;可以让你的开发之路充满激情和动力&#xff0c;让你真正投入其中。 常见的Python的IDE工具有&#xff1a; PyCharm 由JetBrains开发的Python IDE&#xff0c;功能强大&#xff0c;支持调试、代码自动完成、…

安卓Termux搭建web服务器【公网远程手机Android服务器】

文章目录 概述1.搭建apache2.安装cpolar内网穿透3.公网访问配置4.固定公网地址5.添加站点 概述 Termux是一个Android终端仿真应用程序&#xff0c;用于在 Android 手机上搭建一个完整的Linux 环境&#xff0c;能够实现Linux下的许多基本操作&#xff0c;不需要root权限Termux就…

国产集成开发环境工具 CEC-IDE

本周&#xff0c;国内首款适配国产操作系统、自主可控的集成开发环境工具 CEC-IDE 终于开放下载了。公开报道显示&#xff0c;这款集成开发环境工具由数字广东公司联合麒麟软件打造&#xff0c;于今年 6 月份首次亮相。本周&#xff0c;软件上线仅几天内就在知乎和 GitHub 上引…

基于springboot绩效管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

Jetpack业务架构—四件套(Lifecycle、ViewModel、LiveData、DataBinding)

Jetpack 是一个由多个库组成的套件&#xff0c;可帮助开发者遵循最佳做法、减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码&#xff0c;让开发者可将精力集中于真正重要的编码工作。 Android Jetpack组件的优势&#xff1a; Jetpack推出的主要目的是为了能够…

微服务容错 Resilience4j 接口服务-容错原理

微服务容错 Resilience4j 容错原理 4.1 微服务容错简介 在⾼并发访问下&#xff0c;⽐如天猫双11&#xff0c;流量持续不断的涌⼊&#xff0c;服务之间的相互调⽤频率突然增加&#xff0c;引发系统负载过⾼&#xff0c;这时系统所依赖的服务的稳定性对系统的影响⾮常⼤&#…

过滤器的应用-Filter

过滤器 1.工作原理 2.创建Filter 2.1通过注解的方式实现 //创建一个类&#xff0c;实现Filter接口 WebFilter(urlPatterns "/myfilter") //urlPatterns表示需要拦截的路径 public class MyFilter implements Filter {Overridepublic void doFilter(ServletReques…

WebRTC音视频通话-WebRTC推拉流过程中日志log输出

WebRTC音视频通话-WebRTC推拉流过程中日志log输出 之前实现iOS端调用ossrs服务实现推拉流流程。 推流&#xff1a;https://blog.csdn.net/gloryFlow/article/details/132262724 拉流&#xff1a;https://blog.csdn.net/gloryFlow/article/details/132417602 在推拉流过程中的…

arm64架构的linux中断分析

文章目录 1. 中断的概念和作用2. Linux中断处理机制2.1 中断请求2.2 中断处理2.3 中断完成2.4.中断触发和处理步骤详解2.4.1 异常向量表的解读 3. GICv3中断控制器3.1 GICv3中断控制器设备树3.2 GICv3中断控制器驱动 4. GIC的下一级中断控制器4.1 设备树4.2 内核对设备树的处理…

大数据学习:Hive常用函数

Hive常用函数 1. Hive的参数传递 1.1 Hive命令行 查看hive命令的参数 [hadoopnode03 ~]$ hive -help语法结构: hive [-hiveconf xy]* [<-i filename>]* [<-f filename>|<-e query-string>][-S] 说明&#xff1a; -i 从文件初始化HQL。-e从命令行执行指定…

线性代数的学习和整理16:什么是各种空间(类型),向量空间,距离(类型)?

目录 1 空间相关的群&#xff0c;环&#xff0c;域&#xff0c;集合&#xff0c;空间的预备知识 1.1&#xff1a;群&#xff0c;环&#xff0c;域&#xff0c;集合&#xff0c;空间的定义&#xff08;表示不懂&#xff0c;只是做个标记&#xff09; 2 空间 2.1 各种空间概念…

WebRTC-Streamer交叉编译

WebRTC-Streamer交叉编译 flyfish 文章目录 WebRTC-Streamer交叉编译零、前言一、提前准备工作1 安装需要的工具2 可选的交叉编译工具3 默认执行python是python34 获取源码5 使用其他版本的方法 二、非交叉编译编译1 在 src目录执行 安装所需的依赖2 执行命令 三、 交叉编译1 …

【Linux】redhat7.8配置yum在线源【redhat7.8镜像容器内配置yum在线源】通用

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; &#x1f40b; 希望大家多多支…

Navicat 强大的数据模型功能 | 面向数据库设计、架构和数据资产梳理等使用场景

数据模型是用来描述数据、组织数据和对数据进行操作的一组概念和定义。根据不同的应用需求&#xff0c;数据模型可以分为概念模型、逻辑模型和物理模型。这些数据模型帮助数据库设计人员设计和管理数据库&#xff0c;以满足用户的需求。 Navicat 强大的数据模型功能主要适用于…

软件定义网络:重新定义云计算网络架构

文章目录 软件定义网络的基本概念软件定义网络的工作原理软件定义网络在云计算中的应用与优势示例&#xff1a;软件定义网络配置未来发展和挑战结论 &#x1f389;欢迎来到AIGC人工智能专栏~软件定义网络&#xff1a;重新定义云计算网络架构 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&a…

贷款公司如何精准获客,大数据获客

近年来&#xff0c;贷款中介机构在金融服务领域发挥着越来越重要的作用。随着时代的发展&#xff0c;贷款中介机构不仅是贷款服务的提供者&#xff0c;也是能够帮助客户更准确获取客户的服务提供者。 为此&#xff0c;贷款中介机构应把握以下几个方面。 首先&#xff0c;贷款…