当前位置: 首页 > article >正文

FHQ平衡树

FHQ平衡树

大致是这样的题目:

您需要动态地维护一个可重集合 M M M,并且提供以下操作:

  1. M M M 中插入一个数 x x x
  2. M M M 中删除一个数 x x x(若有多个相同的数,应只删除一个)。
  3. 查询 M M M 中有多少个数比 x x x 小,并且将得到的答案加一。
  4. 查询如果将 M M M 从小到大排列后,排名位于第 x x x 位的数。
  5. 查询 M M M x x x 的前驱(前驱定义为小于 x x x,且最大的数)。
  6. 查询 M M M x x x 的后继(后继定义为大于 x x x,且最小的数)。

对于操作 3,5,6,不保证当前可重集中存在数 x x x

对于一颗二叉搜索树来说,确实支持上述所有操作,但是如果构造一组递增或者递减序列,那么二叉搜索树就会退化成一条链,那这样时间复杂度也就退化成了 O ( n ) O(n) O(n) ,这显然不是我们想要的。

所以考虑平衡树,平衡树是指左右儿子大小相差不超过一的二叉搜索树。但是传统的平衡树的旋转很麻烦,所以这里采用一种无旋平衡树的实现方式。

FHQ平衡树,在每个节点不仅维护它的权值的时候,还维护一个 k e y key key 值,然后我们规定除了权值需要满足 左儿子<根节点<右儿子 外,仍需满足 k e y key key 值 :根节点>左儿子 且 根节点 >右儿子。

也就是说对于权值,我们使这棵树满足二叉搜索树的性质,对于 k e y key key 值,我们使这棵树满足大根堆的性质。可以证明的是,如果 k e y key key 值足够随机,那么这颗树的形态就是唯一的。所以我们只需要构造 k e y key key 值使得其满足随机性即可。

接下来需要掌握的前置知识是:

  1. 对于一颗FHQ平衡树,如何将其分裂为 ≤ v \leq v v > v \gt v >v 的两部分。

    有点类似动态开点的思想,分裂的时候 如果当前根节点的权值 ≤ v \leq v v ,那么根节点及其左子树均可以分到左边的部分,然后递归访问其右儿子继续分即可。反之可以将根节点及其右子树分给右边的部分,然后递归访问左儿子继续分。注意:在此过程中,需要动态的修改某些节点的左右儿子,所以用传地址的方式,具体详见代码。

  2. 对于两颗FHQ平衡树,如何将其合并。

    考虑到两颗树,一颗为A树(左),一颗为B树(右),必定有一颗树的根节点是总树的根节点,并且由于二叉搜索树的性质,两棵树的相对位置必定是不变的。那么如果A树的根节点的 k e y key key 值 大于 B树的根节点的 k e y key key 值,那么A树根节点及其左儿子是不用动的,只需要将B树放到A的右儿子上即可,但是A树的右儿子怎么办呢,我们可以让A树的右子树和B树再进行合并,实质上就是一次递归了。反之亦然。搞定!

接下来我们搞定分裂和合并了,那么插入删除以及其他操作不就解决了吗!(bushi)

  1. 考虑插入权值为 v v v 的节点

    只需要将原树分裂成 ≤ v \leq v v > v \gt v >v 的两部分,然后新建节点,三部分按次序合并即可。

  2. 删除权值为 v v v 的节点:

    只需要分裂为 < v 、 = v 、 > v \lt v 、 =v 、 \gt v <v=v>v 的三部分即可,如果删除权值为 v v v 的所有节点,那么只需合并 < v 、 > v \lt v 、 \gt v <v>v 的部分即可,如果删除单个,那么只需要删除 = v =v =v 树的根节点,即需要合并这个部分的左右子树(不合并根节点)不就相当于删除单个节点了吗。

  3. 找出多少个数比 x x x 小:

    即分裂为 ≤ x \leq x x 的部分的树的 s i z e size size

其余的操作同理,过于简单就不说了。

FHQ实现文艺平衡树

您需要写一种数据结构(可参考题目标题),来维护一个有序数列。

其中需要提供以下操作:翻转一个区间,例如原有序序列是 5 4 3 2 1 5\ 4\ 3\ 2\ 1 5 4 3 2 1,翻转区间是 [ 2 , 4 ] [2,4] [2,4] 的话,结果是 5 2 3 4 1 5\ 2\ 3\ 4\ 1 5 2 3 4 1

这个问题,实质上就不能按权值进行分裂了,我们可以按照树的 s i z e size size 进行分裂,这样是不会改变元素相对位置的。

对于一颗FHQ平衡树来说,维护有序区间只需要按次序插入即可,按 s i z e size size 分裂也可以随时分裂出这个序列的任何一个子区间,那么这个题目就简单了。

对于翻转 [ l , r ] [l,r] [l,r] 这段区间来说,只需要先分裂出这段区间,然后由于中序遍历是可以得出这段序列的,那翻转就是将每个节点的左右子树交换即可,但是这样的时间复杂度太高,我们可以像线段树一样在每个节点上维护一个懒标记,需要的时候下传即可。

需要注意的是,每次 s p l i t split split 以及 m e r g e merge merge 可能会改变节点的左右儿子,所以我们需要在 s p l i t split split m e r g e merge merge 前进行标记的下传。

代码:

mt19937 rnd(time(NULL));
struct node{int sz,v,key,l,r,f;node(){this->sz=this->v=this->key=this->l=this->r=this->f=0;this->key=rnd();}node(int v){this->sz=this->v=this->key=this->l=this->r=this->f=0;this->sz=1;this->key=rnd();this->v=v;}
};
struct fhq{node tr[100020];int tot=0,root=0;void pushup(int root){tr[root].sz=tr[tr[root].l].sz+tr[tr[root].r].sz+1;}void pushdown(int root){if(!tr[root].f)return ;swap(tr[root].l,tr[root].r);tr[root].f^=1;tr[tr[root].l].f^=1;tr[tr[root].r].f^=1;}void split(int root,int v,int &x,int &y){//v分裂if(!root){x=y=0;return ;}pushdown(root);if(tr[root].v<=v){x=root;split(tr[root].r,v,tr[root].r,y);}else {y=root;split(tr[root].l,v,x,tr[root].l);}pushup(root);}void split_sz(int root,int v,int &x,int &y){//sz分裂if(!root){x=y=0;return ;}pushdown(root);if(tr[tr[root].l].sz+1<=v){x=root;split(tr[root].r,v-(tr[tr[root].l].sz+1),tr[root].r,y);}else {y=root;split(tr[root].l,v,x,tr[root].l);}pushup(root);}int merge(int x,int y){if(!x||!y)return x|y;if(tr[x].key<tr[y].key){pushdown(x);tr[x].r=merge(tr[x].r,y);pushup(x);return x;}// else {pushdown(y);tr[y].l=merge(x,tr[y].l);pushup(y);return y;// }}void insert(int v){int x,y;split(this->root,v-1,x,y);tr[++tot]=node(v);this->root=merge(merge(x,tot),y);}void del(int v){int x,y,z;split(this->root,v-1,x,y);// x [1,v-1]split(y,v,y,z);// y [v,v] z [v+1,n]// 删除单个vthis->root=merge(x,merge(tr[y].l,tr[y].r));this->root=merge(this->root,z);}int find_min(int v){//查询<v的个数int x,y;split(this->root,v-1,x,y);int ans=tr[x].sz;this->root=merge(x,y);return ans;}int find_xth(int x){//查询排名第x位的数int now=this->root;while(now){if(tr[tr[now].l].sz+1==x)return tr[now].v;if(tr[tr[now].l].sz+1>x){now=tr[now].l;}else {x-=tr[tr[now].l].sz+1;now=tr[now].r;}}return -1; // 无}int find_pre(int v){// 找小于v最大的int x,y;split(this->root,v-1,x,y);int now=x;while(tr[now].r)now=tr[now].r;this->root=merge(x,y);return tr[now].v;}int find_suf(int v){// 找大于v最小的int x,y;split(this->root,v,x,y);int now=y;while(tr[now].l)now=tr[now].l;this->root=merge(x,y);return tr[now].v;}void reverse(int l,int r){int x,y,z;split(this->root,l-1,x,y);// x [1,l-1]split(y,r-l+1,y,z);// y [l,r]  z [r+1,n]tr[y].f^=1;this->root=merge(merge(x,y),z);}void print(int root){if(!root)return ;pushdown(root);print(tr[root].l);cout<<tr[root].v<<' ';print(tr[root].r);}
}T;

相关文章:

FHQ平衡树

FHQ平衡树 大致是这样的题目&#xff1a; 您需要动态地维护一个可重集合 M M M&#xff0c;并且提供以下操作&#xff1a; 向 M M M 中插入一个数 x x x。从 M M M 中删除一个数 x x x&#xff08;若有多个相同的数&#xff0c;应只删除一个&#xff09;。查询 M M M 中…...

力扣算法---总结篇

5.13 数组总结 数组是存放在连续内存空间上的相同类型数据的集合。 数组可以方便的通过下标索引的方式获取到下标对应的数据。 正是因为数组在内存空间的地址是连续的&#xff0c;所以我们在删除或者增添元素的时候&#xff0c;就难免要移动其他元素的地址。 数组的元素是不…...

ABAP+旧数据接管的会计年度未确定

导资产主数据时&#xff0c;报错旧数据接管的会计年度未确定 是因为程序里面使用了下列函数AISCO_CALCULATE_FIRST_DAY&#xff0c;输入公司代码&#xff0c;获取会计年度&#xff0c;这个数据是在后台表T093C表中取数的&#xff0c;通过SE16N可以看到后台表数据没有数&#xf…...

Java【10_1】用户注册登录(面向过程与面向对象)

测试题 1、基于文本界面实现登录注册的需求(要求可以满足多个用户的注册和登录) 通过工具去完成 公共类&#xff1a; public class User { private int id;//用户编号 private int username;//用户名 private int password;//密码 private String name;//真…...

养生:打造健康生活的全方位策略

在生活节奏不断加快的当下&#xff0c;养生已成为提升生活质量、维护身心平衡的重要方式。从饮食、运动到睡眠&#xff0c;再到心态调节&#xff0c;各个方面的养生之道共同构建起健康生活的坚实基础。以下为您详细介绍养生的关键要点&#xff0c;助您拥抱健康生活。 饮食养生…...

贪吃蛇游戏排行榜模块开发总结:从数据到视觉的实现

一、项目背景与成果概览 在完成贪吃蛇游戏核心玩法后,本次开发重点聚焦于排行榜系统的实现。该系统具备以下核心特性: 🌐 双数据源支持:本地存储(localStorage)与远程API自由切换 🕒 时间维度统计:日榜/周榜/月榜/全时段数据筛选 🎮 模式区分:闯关模式(关卡进度…...

pytorch 数据预处理和常用工具

文章目录 NumPyNumpy数据结构安装和使用NumPy Matplotlib的安装和导入安装和导入Matplotlib绘制基础图画折线图散点图柱状图图例 数据清洗据清洗的作用Pandas进行数据清洗Pandas数据结构Series 数据结构DataFrame数据结构 Pandas数据清洗常用代码 特征工程主成分分析线性判别分…...

如何界定合法收集数据?

首席数据官高鹏律师团队 在当今数字化时代&#xff0c;数据的价值日益凸显&#xff0c;而合法收集数据成为了企业、机构以及各类组织必须严守的关键准则。作为律师&#xff0c;深入理解并准确界定合法收集数据的范畴&#xff0c;对于保障各方权益、维护法律秩序至关重要。 一…...

企业对数据集成工具的需求及 ETL 工具工作原理详解

当下&#xff0c;数据已然成为企业运营发展过程中的关键生产要素&#xff0c;其重要性不言而喻。 海量的数据分散在企业的各类系统、平台以及不同的业务部门之中&#xff0c;企业要充分挖掘这些数据背后所蕴含的巨大价值&#xff0c;实现数据驱动的精准决策&#xff0c;数据集…...

内核深入学习3——分析ARM32和ARM64体系架构下的Linux内存区域示意图与页表的建立流程

内核深入学习3——ARM32/ARM64在Linux内核中的实现&#xff08;2&#xff09; ​ 今天我们来讨论的是一个硬核的内容&#xff0c;也是一个老生常谈的话题——那就是分析ARM32和ARM64体系架构下的Linux内存区域示意图的内容。对于ARM64的部分&#xff0c;我们早就知道一个基本的…...

MapReduce基本介绍

核心思想 分而治之&#xff1a;将大规模的数据处理任务分解成多个可以并行处理的子任务&#xff0c;然后将这些子任务分配到不同的计算节点上进行处理&#xff0c;最后将各个子任务的处理结果合并起来&#xff0c;得到最终的结果。 工作流程 Map 阶段&#xff1a; 输入数据被…...

屏幕与触摸调试

本章配套视频介绍: 《28-屏幕与触摸设置》 【鲁班猫】28-屏幕与触摸设置_哔哩哔哩_bilibili LubanCat-RK3588系列板卡都支持mipi屏以及hdmi显示屏的显示。 19.1. 旋转触摸屏 参考文章 触摸校准 参考文章 旋转触摸方向 配置触摸旋转方向 1 2 # 1.查看触摸输入设备 xinput…...

使用 百度云大模型平台 做 【提示词优化】

1. 百度云大模型平台 百度智能云千帆大模型平台 &#xfeff; 平台功能&#xff1a;演示了阿里云大模型的百炼平台&#xff0c;该平台提供Prompt工程功能&#xff0c;支持在线创建和优化Prompt模板模板类型&#xff1a;平台提供多种预制模板&#xff0c;同时也支持用户自定义…...

C 语言_常见排序算法全解析

排序算法是计算机科学中的基础内容,本文将介绍 C 语言中几种常见的排序算法,包括实现代码、时间复杂度分析、适用场景和详细解析。 一、冒泡排序(Bubble Sort) 基本思想:重复遍历数组,比较相邻元素,将较大元素交换到右侧。 代码实现: void bubbleSort(int arr[], i…...

IJCAI 2025 | 高德首个原生3D生成基座大模型「G3PT」重塑3D生成的未来

国际人工智能联合会议&#xff08;IJCAI&#xff09;是人工智能领域最古老、最具权威性的学术会议之一&#xff0c;自1969年首次举办以来&#xff0c;至今已有近六十年的历史。它见证了人工智能从萌芽到蓬勃发展的全过程&#xff0c;是全球人工智能研究者、学者、工程师和行业专…...

Samtec助力电视广播行业

【摘要前言】 现代广播电视技术最有趣的方面之一就是界限的模糊。过去&#xff0c;音频和视频是通过射频电缆传输的模拟技术采集的&#xff0c;而现在&#xff0c;数字世界已经取代了模拟技术。物理胶片和磁带已让位于数字存储设备和流媒体。 在这个过程中&#xff0c;连接器…...

密码学--仿射密码

一、实验目的 1、通过实现简单的古典密码算法&#xff0c;理解密码学的相关概念 2、理解明文、密文、加密密钥、解密密钥、加密算法、解密算法、流密码与分组密码等。 二、实验内容 1、题目内容描述 ①随机生成加密密钥&#xff0c;并验证密钥的可行性 ②从plain文件读入待…...

生成式图像水印研究综述

生成式图像水印研究综述 一、引言二、生成式图像水印研究背景三、生成式图像水印算法研究进展3.1 基于流模型的方案3.2 基于生成对抗网络的方案3.3 基于扩散模型的方案3.3.1 修改图像数据3.3.2 调整生成模型3.3.3 修改隐变量空间四、算法的性能与评价指标五、常用数据集六、本章…...

TCP协议详细讲解及C++代码实例

目录 一. TCP协议详细讲解及C代码实例1、TCP协议概述2、TCP通信流程1&#xff09; 三次握手2) 数据传输3) 四次挥手 3、关键点解析1&#xff09; 套接字创建2&#xff09; 三次握手实现3&#xff09; 数据传输4&#xff09; 四次挥手实现 4、TCP与UDP对比 一. TCP协议详细讲解及…...

深度剖析:Vue2 项目兼容第三方库模块格式的终极解决方案

当我们为 Vue2 项目引入某些现代 JavaScript 库时&#xff0c;常常会遇到这样的报错&#xff1a; error in ./node_modules/some-lib/lib/index.mjs Cant import the named export xxx from non EcmaScript module这类问题的本质是模块格式的世纪之争 —— ES Module&#xff…...

APISQL免费版安装教程(视频)

APISQL 一款通用的API开发管理软件&#xff0c;支持将主流数据库中的表、视图、SQL语句、存储过程等快速封装为标准的 RESTful API&#xff0c;支持多种安全认证方式和可视化管理界面。适用于接口开发、系统集成、数据共享等场景。 支持主流数据库的表、视图、自定义函数、存储…...

SpringBoot整合MQTT实战:基于EMQX实现双向设备通信(附源码)

简言&#xff1a; 在万物互联的时代&#xff0c;MQTT协议凭借其轻量级、高效率的特性&#xff0c;已成为物联网通信的事实标准。本教程将带领您在Ubuntu系统上搭建EMQX 5.9.0消息服务器&#xff0c;并使用Spring Boot快速实现两个客户端的高效通信。通过本指南&#xff0c;您将…...

从零开始掌握FreeRTOS(2)链表之节点的定义

目录 节点 节点定义 节点实现 根节点 根节点定义 精简节点定义 根节点实现 在上篇文章,我们完成了 FreeRTOS 的移植。在创建任务之前,我们需要先了解FreeRTOS的运转机制。 FreeRTOS是一个多任务系统,由操作系统来管理执行每个任务。这些任务全都挂载到一个双向循…...

Java的While循环写的出票简单程序

import java.util.Scanner;public class Hello {public static void main(String[] args) {Scanner in new Scanner(System.in);int balance 0;while(true){System.out.print("请投币: ");int amount in.nextInt();balance balance amount;if(balance >10 )…...

详解Windows(十一)——网络连接设置

Windows网络连接设置完全指南 1. Windows网络连接基础 网络连接类型 有线连接&#xff1a; 通过网线将电脑连接到路由器或调制解调器优点&#xff1a;连接稳定&#xff0c;速度快&#xff0c;延迟低适合&#xff1a;需要高速稳定网络的场景&#xff0c;如游戏、大文件下载、…...

多线程爬虫语言选择与实现

之前文中有人提到&#xff1a;想要一个简单易用、能快速实现多线程爬虫的方案&#xff0c;而且目标是小网站&#xff0c;基本可以确定对反爬虫措施要求不高&#xff0c;这些就比较简单了。 以往我肯定要考虑常见的编程语言中哪些适合爬虫。Python、JavaScript&#xff08;Node…...

【数据结构】——双向链表

一、链表的分类 我们前面学习了单链表&#xff0c;其是我们链表中的其中一种&#xff0c;我们前面的单链表其实全称是单向无头不循环链表&#xff0c;我们的链表从三个维度进行分类&#xff0c;一共分为八种。 1、单向和双向 可以看到第一个链表&#xff0c;其只能找到其后一个…...

AI助力:零基础开启编程之旅

一、代码调试 三步解决BUG 1. 错误信息翻译 指令模板&#xff1a; 错误诊断模式我遇到【编程语言】报错“粘贴报错信息“ 请&#xff1a; 用小白能懂的话解释问题本质标注可能引发该错误的三个场景给出最可能的修复方案和其他备选方案 2. 上下文分析 进阶指令 结合上下文代…...

mybatis中${}和#{}的区别

先测试&#xff0c;再说结论 userService.selectStudentByClssIds(10000, "wzh or 11");List<StudentEntity> selectStudentByClssIds(Param("stuId") int stuId, Param("field") String field);<select id"selectStudentByClssI…...

【计算机组成原理】第二部分 存储器--分类、层次结构

文章目录 分类&层次结构0x01 分类按存储介质分类按存取方式分类按在计算机中的作用分类 0x02 层次结构 分类&层次结构 0x01 分类 按存储介质分类 半导体存储器磁表面存储器磁芯存储器光盘存储器 按存取方式分类 存取时间与物理地址无关&#xff08;随机访问&#…...