平衡树初步——Treap

FHQ-Treap(非旋treap/平衡树)——从入门到入坟 平衡树有很多种,其中fhq-treap 可以说是最强的一种平衡树之一,它可以维护值域,也可以维护下标,还能维护区间修改,更难能可贵的是,它可以完成splay都完成不了的可持久化操作。其唯一弱于splay的就是在LCT上,splay维护LCT的复杂度是O(nlog(n)),而fhq-treap的复杂度为O(nlog^2(n)),稍微大了一点,但是比splay好写多了。............ 阅读详情

平衡树算是比较进阶一点的数据结构,这里只是对学习简单平衡树的记录

BST——二叉查找树

书上说,二叉树最重要的两种数据结构的性质有两种:堆性质 和 BST性质

二叉查找树满足以下性质:

  • 每个节点都有一个关键值
  • 每个节点的关键值大于其左子节点的关键值
  • 每个节点的关键值小于其右子节点的关键值

由于每个节点是顺序插入的,所以我们容易知道,一个节点的左子树的所有节点一定小于该节点,右子树的所有节点一定大于该节点

后继

大于某个节点的最小节点

前驱

小于某个节点的最大节点

BST的主要操作

  • 检索
  • 插入
  • 求前驱后继等

在学习平衡树初期,对于普通平衡树我打了一个三百多行的BST实现代码,实现的细节和原理相当复杂,不便于修改和模板式记忆,所以我在写这篇博客的同时,将根据资料书写出更为简单的BST实现

代码实现

#include <cstdio>
#include <iostream>
#include <cmath>
#include <ctime>

#define MAX_ 0x7fffffff

using namespace std;

struct BST
{
    int l;
    int r;
    int val;
    int size;
    int num;
    int dat;
} a[100000];

int tot;
int root;

int NEW(int val)
{
    a[++tot].val = val;
    a[tot].num = 1;
    a[tot].size = 1;
    a[tot].dat = rand();
    return tot; // return the id of a new node
}
void update(int p)
{
    a[p].size = a[a[p].l].size + a[a[p].r].size + a[p].num;
}
void Build()
{
    NEW(-MAX_);
    NEW(MAX_); // building with an infinity node
               // this is a step that needed for the treap
               // to avoid rotate the infinity node to go down
    a[1].r = 2;
    root = 1;
    update(root);
}

int getVal_byRank(int p, int rank)
{
    if (p == 0)
    {
        return -1; // no such a rank
    }
    if (rank <= a[a[p].l].size)
    {
        return getVal_byRank(a[p].l, rank);
    }
    else if (rank > a[p].size - a[a[p].r].size)
    {
        return getVal_byRank(a[p].r, rank - a[a[p].l].size - a[p].num);
    }
    else
    {
        return a[p].val;
    }
}

int getRank_byVal(int p, int val)
{
    if (p == 0)
    {
        return 0;
    }
    if (val > a[p].val)
    {
        return getRank_byVal(a[p].r, val) + a[a[p].l].size + a[p].num;
    }
    else if (val < a[p].val)
    {
        return getRank_byVal(a[p].l, val);
    }
    else
    {
        return a[a[p].l].size + 1;
    }
}

void Rrotate(int &p)
{
    int q = a[p].l;
    a[p].l = a[q].r;
    a[q].r = p;
    p = q;
    update(a[p].r);
    update(p);
}
void Lrotate(int &p)
{
    int q = a[p].r;
    a[p].r = a[a[p].r].l;
    a[q].l = p;
    p = q;
    update(a[p].l);
    update(p);
}

void insert(int &p, int val) // p is the reference of the father's pointer towards this node
{
    // cout << p << ' ' << a[p].l << ' ' << a[p].val << ' ' << val << endl;
    //   as well it's the id of this node
    if (p == 0)
    {
        p = NEW(val); // there is no such a node so create one and set the father's pointer
        return;
    }
    if (val == a[p].val)
    {
        a[p].num++;
        update(p);

        return;
    }
    else if (val < a[p].val)
    {
        insert(a[p].l, val); // this way,we can set the l while passing down the id of the current node' child
        if (a[p].dat < a[a[p].l].dat)
        {
            // cout << "Y";
            Rrotate(p);
        }
    }
    else if (val > a[p].val)
    {
        insert(a[p].r, val);
        if (a[p].dat < a[a[p].r].dat)
        {
            // cout << "N";
            Lrotate(p);
        }
    }
    update(p);
}

int getPre(int val)
{
    int ans = -1e9;
    int p = root;
    while (true)
    {if (p == 0)
        {
            return ans;
        }
        if (a[p].val < val)
        {
            ans = max(ans, a[p].val);
        }

        
        if (a[p].val == val)
        {
            if (a[p].l == 0)
            {
                return ans;
            }
            p = a[p].l;
            while (a[p].r != 0)
            {
                p = a[p].r;
            }
            return a[p].val;
        }
        p = a[p].val > val ? a[p].l : a[p].r;
    }
}

int getNext(int val)
{
    int ans = 1e9;
    int p = root;
    while (true)
    {
        if (p == 0)
        {
            return ans;
        }//this place must be placed before the "min"
         //otherwise the mininus is must be '0'
        if (a[p].val > val)
        {
            ans = min(ans, a[p].val);
        }

        if (a[p].val == val)
        {
            if (a[p].r == 0)
            {
                return ans;
            }
            p = a[p].r;
            while (a[p].l != 0)
            {
                p = a[p].l;
            }
            return a[p].val;
        }
        p = a[p].val > val ? a[p].l : a[p].r;
    }
}

void remove(int &p, int val)
{
    if (p == 0)
    {
        return;
    }
    if (val == a[p].val)
    {
        if (a[p].num > 1)
        {
            a[p].num--;
            update(p);
            return;
        }
        if (a[p].l || a[p].r)
        {
            if (a[p].r == 0 || a[a[p].l].dat > a[a[p].r].dat)
            {
                Rrotate(p);
                remove(a[p].r, val);
            }
            else
            {
                Lrotate(p);
                remove(a[p].l, val);
            }
            update(p);
        }
        else
        {
            p = 0;
        }
        return;
    }
    a[p].val > val ? remove(a[p].l, val) : remove(a[p].r, val);
    update(p);
}

void tra(int p)
{
    if (p == 0)
        return;
    cout << a[p].val << ' ' << a[p].size << endl;
    tra(a[p].l);
    tra(a[p].r);
}

int main()
{
    Build();
    srand(time(0));

    int n;
    cin >> n;
    for (int i = 0; i < n; i++)
    {
        int op, x;
        cin >> op >> x;
        // cout << op << ' ';
        switch (op)
        {
        case 1:
            insert(root, x);
            break;
        case 2:
            remove(root, x);
            break;
        case 3:
            cout << getRank_byVal(root, x) - 1 << endl;
            break;
        case 4:
            cout << getVal_byRank(root, x + 1) << endl;
            break;
        case 5:
            cout << getPre(x) << endl;
            break;
        case 6:
            cout << getNext(x) << endl;
            break;
        case 7:
            tra(root);
            cout << tot << endl;
            break;
        default:
            break;
        }
    }
}
  • 在以上代码中,所有的传递的p为引用时,说明传递的参数必须是对应父节点的左右节点,这样在修改p时可以一同修改父节点的子节点信息了
  • 对于 insert(),remove()之类的操作需要更新祖先节点的size大小,所以需要采用递归的方式修改,而对于查询之类的操作不需要更新祖先节点数值,所以采用循环的方式更加快速
  • 这是一道平衡树的模板题,但是仍然免不了将近三百行的代码
偷懒专用平衡树——Treap Treap的实现 阅读详情

相关推荐

简析平衡树(二)——Treap

前言 学完了替罪羊树,我决定再去学一学TreapTreapTreap。一直听说TreapTreapTreap很难,我也花了挺久才学会。 简介 TreapTreapTreap这个名字真的挺有内涵: TreeTree\color{red}{Tree}+HeapHeap\color{blue}{Heap}=TreTre\color{red}{Tre}+eapeap\color{blue}{e...

CXR的博客 4674

二叉查找树与平衡树初步(Treap):

例题:洛谷 P3369 【模板】普通平衡树 题目描述 您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作: 1。插入x数 2。删除x数(若有多个相同的数,因只删除一个) 3。查询x数的排名(排名定义为比当前数小的数的个数+1。若有多个相同的数,因输出最小的排名) 4。查询排名为x的数 5。求x的前驱(前驱定义为小于x,且最大的数) 6。求x的后继(后继定义为大于x,且最小的...

_Griefs 199

无旋treap(可持久化,区间操作),解决普通平衡树、文艺平衡树

无旋treap 普通平衡树tyvj1728(treap模板) 如果想看旋转treap怎么写戳这儿 无旋treap代码: #include<bits/stdc++.h> using namespace std; const int N=5e5+5; struct node{ int val,ch[2],size,rd; #define l(x) t[x].ch[0] #define...

avenLJY的博客 1021

最强平衡树——Treap[以我的最弱击败你的最强]

以我的最弱战胜你的最强!!——Treap本人蒟蒻,在平衡树坑中深陷数年。为了早日逃离此天坑,特作此文。什么是平衡树?度娘传送门 什么是treap?ACdreamers%%% 注:本篇所有代码都在片尾!!(醒目)CMP那么了解了这些,我们先列出一个list NAME 优势 劣势 splay LCT,序列之王 常数大,代码量稍大 RBT 自适应深度平衡树,速度在同类BS

lemonoil的博客 955

模板 - FHQ - treap 无旋平衡树

整理的算法模板合集: ACM模板 #include<bits/stdc++.h> using namespace std; const int N = 100007; int n, m, tot, root; struct node{ int l, r; int key;//权值 int val;//平衡因子 int size;//子树个数 }tr[N]; void pushup(int p){ tr[p].size = tr[tr[p].l]

繁凡さん的博客 1073

Python地理空间库安装指南:从gdal到geopandas的完整流程

本文详细介绍了Python地理空间库的完整安装流程,包括gdal、rasterio和geopandas等核心库的安装与配置。通过Anaconda环境管理和conda-forge频道,帮助开发者高效解决依赖问题,确保地理空间数据处理环境的稳定运行。

weixin_29091105的博客 462

【平衡树】FHQ-Treap讲解

FHQ-Treap

abs20187的博客 1102

学习STM32的无人机控制

设计并实现无人机控制系统,使用STM32单片机进行控制。无人机控制系统主要包括飞行控制、姿态控制、定位导航等功能。以下是一个基于STM32的无人机控制系统的代码案例。以上是一个基于STM32的无人机控制系统的代码案例,包括飞行控制、姿态控制和定位导航等功能。请根据实际情况进行修改和完善。

qq_67153941的博客 837

数据结构之平衡树(Treap)

平衡树是二叉搜索树和堆合并构成的新数据结构,所以它的名字取了Tree和Heap各一半,叫做Treap。 堆和树的性质是冲突的,二叉搜索树满足左子树<根节点<右子树,而堆是满足根节点小于等于(或大于等于)左右儿子。因此在Treap的数据结构中,并不是以单一的键值作为节点的数据域。Treap每个节点的数据域包含2个值,key和weight。 key值,和原来的二叉搜索树一样,满足左子树<根节点<右子

long-long-ago 2万+

蛋白质组学进阶指南 | (八)差异蛋白筛选与功能解析实战

本文是蛋白质组学差异蛋白筛选与功能解析的实战指南。详细讲解了如何利用R语言的limma包进行差异表达分析,结合FDR校正和差异倍数设定筛选关键蛋白,并通过火山图可视化结果。进一步介绍了使用clusterProfiler进行GO与KEGG功能富集分析,以及利用STRING数据库和Cytoscape构建蛋白互作网络以识别枢纽分子,最终串联分析形成可验证的生物学假说。

weixin_29208077的博客 739

软件工程导论考试通关秘籍:太原理工大学历年真题解析(附答案)

本文为太原理工大学《软件工程导论》考试提供高效备考指南,通过解析近六年真题,揭示高频考点如软件生命周期、UML建模和结构化设计等核心模块的命题规律。文章提供真题拆解、实战策略和避坑指南,帮助考生掌握图形转换题和简答题的应答技巧,助力考试通关。

weixin_29233333的博客 227

Treap【普通平衡树】

Treap是一种结合二叉搜索树(BST)和二叉堆(Binary Heap)特性的数据结构。它通过随机优先级(key)来维持平衡,支持插入、删除、查询排名、查询值、查找前驱和后继等操作。关键操作包括左旋(zag)和右旋(zig),用于在插入和删除时维护堆的性质。Treap的每个节点包含左右子节点指针、值(val)、子树大小(size)、出现次数(tot)和随机优先级(key)。插入时根据值比较决定方向,并通过旋转保持平衡;删除时需处理节点替换和再次平衡。这种结构在保证BST有序性的同时,通过随机优先级实现近似

ywh012805的博客 1275

基于FaceX-Zoo实现的Face Mask Adding人脸戴口罩

人脸系列文章目录 文章目录人脸系列文章目录前言一、加口罩效果二、实现原理二、代码实现 前言 目前人脸加口罩均为在人脸位置贴图式的,非常不真实。前段时间京东开源一个基于PRnet实现的3D口罩拟合人脸,加口罩非常逼真,但是只是单独一个加口罩demo,没有完整的流程。于是本人结合人脸检测,关键点对齐。PRnet加口罩实现一个完整的加口罩流程。只需一个命令简单方便调用,实现人脸加口罩。 https://github.com/JDAI-CV/FaceX-Zoo 一、加口罩效果 下图可以看出效果还是非常逼真的

zengwb的博客 3390

Treap平衡树

浅谈Treap平衡树先从二叉查找树谈起Treap平衡树的旋转操作定义建立新节点右旋 先从二叉查找树谈起 二叉查找树可以用来在随机数据中高效率地查找一个数。具有以下性质: (1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值; (2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值; (3)左、右子树也分别为二叉排序树; (4)平均的时间复杂度为O(log n); (摘自360百科...

qq_43041641的博客 276

倒立摆的PLC控制(模型分析+ SCL源代码)

首先简单介绍下倒立摆模型,下面这幅图是MATLAB网站上的倒立摆模型,下面我们利用牛顿第二运动定律建立摆杆的运动方程。

RXXW_Dor的博客 3331

OceanBase迁移平台OMS企业版部署

OceanBase迁移平台OMS企业版部署摘要: 本文档详细介绍了OceanBase迁移平台OMS企业版的部署流程,包含环境准备、OAT部署管理、MetaDB部署等关键步骤。

Sebastien23的博客 521

平衡树——treap

一种是要删除的数在叶子节点上,那么我们直接把这个叶子节点的下标修改成0即可,因为我们在上一层传入的是它父节点的tr[u].l或者tr[u].r的引用,所以这里直接赋成0就相当于将它父节点的左指针或者右指针的指向修改成空;通过前三个赋值,我们已经实现了子节点的交换,相对于原来的p的父节点的更新我们该怎么实现了,显然我们没有存父节点的信息, 不能从子节点访问到父节点,但是我们肯定是在递归中实现的,我们只要在递归的时候传入tr[u].l的引用,那么在这一层进行修改的时候就可以实现对tr[u].l的修改。

m0_74120503的博客 1696

C#实战大华智能安防系统:从SDK封装到视频流与智能报警的全链路落地

本文将介绍C#集成大华SDK开发安防系统的实战经验,重点解决多路视频预览卡顿和报警延迟问题。文章通过三层架构设计(SDK封装层、业务服务层、应用层)实现解耦,详细分析线程安全封装、高效解码显示和智能事件联动等核心技术。针对16路摄像头同时预览时CPU占用率过高的问题,提出使用子码流拉流与异步解码的优化方案,并总结8个常见开发陷阱的解决方案。文中还提供了大华智能安防系统的完整架构图,展示从设备层到应用层的交互流程,特别强调业务服务层中视频流服务和智能报警服务的核心功能设计。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 430

平衡树---Treap

平衡树—Treap 1. Treap原理 原理 平衡树的种类:AVL、RBTree、Splay、Treap、sbt、… Treap=Tree(BST) + Heap 首先来看什么是BST:Binary Search Tree,即二分搜索树。其定义是递归定义,对于每一个点,其权值严格大于其左子树中所有节点的值,严格小于其右子树所有节点的的值。 BST有一个很重要的性质:其中序遍历的结果一定是从小到大的序列。 BST的本质:动态维护一个有序序列(集合)。 这里实现的BST,默认BST中

weixin_42638946的博客 1128

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Treap平衡树学习笔记

Treap平衡树学习笔记 放在前面的话 人类的本质是复读机,Treap的本质是二叉搜索树 Treap杀手锏——自动平衡 为什么要平衡? 树上旋转!!! 自动平衡!!! 小结 Treap操作 定义Treap 旋转 插入 删除 维护子树大小 求排名为kk的元素 求元素xx的排名 求元素的前驱后继 你以为学完Treap了?不!还有FHQ Treap 无需旋转的Treap 超强核心Merge ~~另一核心Split~~万物皆基于Merge 分裂Split 插入Insert 删除Delete 小结×2 放在最后的话

Bell的博客 586

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

各种平衡树Treap/SBT/Avl/Splay tree

平衡树

GadyPu的专栏 4042

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

平衡树之treap

之前的博客中简单地阐述了二叉查找树的概念及简单应用,并介绍了平衡树。接下来我们便开始研究平衡树中实现较为简单的一种——treap的具体写法。一、treap简介介绍treap之前,先允许我来介绍一下笛卡尔树。这种树有两个值,如果只看第一个值(以下称为第一关键字),其满足二叉查找树的性质,而只看第二个值(以下称为第二关键字),其满足堆的性质(即某节点所有子树值比其大或小)。顾名思义,treap=tree

xffyjq的博客 651

平衡树·从BST到treap到fhq-treap

挖坑

NephrenRuqInsania 678

普通平衡树(treap)

前言 您需要写一种数据结构,来维护一些数,其中需要提供一下的操作: 1.插入数值x 2.删除数值x(如果有多个,只删除一个) 3.查询数值x的排名(如有多个相同的数,应输出最小的排名) 4.查询排名为x的数值 5. 求数值x的前驱(小于x最大的数) 6.求数值x的后继(大于x最小的数) 首先我们引入一种数据结构叫二叉查找树(BST)。该树满足一下性质: 对于树的任意一个节点 ...

bbbblzy的博客 6211

【平衡树】旋转式Treap

怎么说,今天重温了平衡树的Treap写法,做了一点心得,稍微总结一下。 首先是模板题: 洛谷P6136 普通平衡树数据加强版 平衡树的功能及个人理解 首先,平衡树的退化版是二叉搜索数,二叉搜索树就不解释了。个人认为平衡树属于一类区间信息查找数据结构,搜索树主要是将带搜索信息存入树结构,借助树结构进行查找和维护,凡是这种支持区间或集合信息维护的数据结构往往能得到应用广泛。平衡树事实上只不过用了各种方法将树高维持在logN以内,保证查找效率而已,所以平衡树本身个人并不认为是一大类,例如Treap的核心是一种“

qq_16016913的博客 541

Treap平衡树学习

Treap平衡树 今天学习了Treap平衡树,记录一下心得。 前导:二叉查找树BST(Binary Search Tree) 概率 二叉查找树(Binary Search Tree)是基于插入思想的一种在线的排序数据结构。 它又叫二叉搜索树(Binary Search Tree)、二叉排序树(Binary Sort Tree),简称 BST。 这种数据结构的基本思想是在二叉树的基础上,...

StaroForgin的博客 346
下一篇: 割边和割点
尘_无下限
博客等级 码龄8年 74粉丝 3原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值