平衡树

  • 平衡树作为一种中级数据结构,有着广泛的使用场景。其平衡性的维护方式灵活多变,而其中的无旋treap更以简单著称

P3369 【模板】普通平衡树

  • 题意:

    需维护以下操作:

    插入一个数 x。

    删除一个数 x(若有多个相同的数,应只删除一个)。

    定义排名为比当前数小的数的个数 +1。查询 x 的排名。

    查询数据结构中排名为 x 的数。

    求 x 的前驱(前驱定义为小于 x,且最大的数)。

    求 x 的后继(后继定义为大于 x,且最小的数)。
  • 平衡树是一种二叉搜索树,而treap将其与堆结合,使其同时具有二者的性质。

    treap通过给每个节点一个随机数值,依靠这个值维护一个堆,保证树的平衡性。

    而无旋treap通过两种关键操作来维护这两者的性质:split和merge

split

  • split将目标树按值分裂成两棵树,由于分裂后需要返回两颗树的根,不好返回,因此用指针&x和&y来传递分裂后的两个根

    由于二叉搜索树的性质:u左子树的节点值都比u的权值小,而右子树的都比u的大。因此如果分裂目标k比u小,那么就去左子树找

    反之亦然

    由于二叉搜索树的性质,分裂出来的x子树中的所有值都比y子树中的所有值小。这个性质在合并时非常关键

    代码:
#define ls (tr[u].l)
#define rs (tr[u].r)
void push_up(int u)
{
tr[u].size=1+tr[ls].siz+tr[rs].siz;
}
void split(int u,int &x,int &y,int k)
{
if(u==0){x=0,y=0;return;}
if(tr[u].val<=k)
{
x=u,split(rs,rs,y,k);
}
else y=u,split(ls,x,ls,k);
push_up(u);
}

容易理解,split并不破坏原treap堆的性质

  • 注意 代码中关于&x与&y的确定需要特别强调。x代表分裂出来权值较小的子树的根节点,y代表分裂出来权值较大的子树的根节点,如下图

  • 如果k比u的值大,证明整个左子树都比k小,因此这时的k就在k1的位置上。

    因此u及其子树k1左边的全是属于其父亲给出的x,即分裂出的较小的子树。而较大的子树还不知道,就到k1右边的橙色部分中找

    而将u的右子树带到split函数中x的位置,是为了更新u的右子树,在分裂完成后将剩下的小于k的部分接回u右子树的空缺

    这个过程中,左子树无影响。分裂完后,u的大小很可能改变,因此要push_up更新大小

    而k在图中左边的原理同理,不再赘述。

merge

  • merge是将两个二叉平衡树合并的函数,要求以x为根的子树中所有元素都必须小于y子树中的元素。这个要求看似苛刻,但正好与split出来的子树的性质不谋而合。
  • 由于合并的两棵树x中所有值都比y小,因此对于二叉搜索树的性质,只需要将大的树接在小的树右下角或者小的接在大的左下角就行了。
  • merge的关键是一个随机值:\(tr[u].rnd\) 。还记得前文说到treap是二叉搜索树与堆的结合吗?split并不破坏堆的性质,而merge正好就根据初始所附的随机值来维护堆的性质。如何维护具体看代码
int merge(int x,int y) //返回的是两树合并后的根节点
{
if(!x||!y) return x+y; //如果x或y中任意一一个为零,就直接以另一个为根
if(tr[x].rnd<tr[y].rnd) //维护的是小根堆。若x的随机值比y的小,那么x就在y上面,x作为根被返回
{
tr[x].r=merge(tr[x].r,y);push_up(x);return x;
}
else
{
tr[y].l=merge(x,tr[y].l);push_up(y);return y;//否则y当根,注意merge中带入的第一棵树值都比第二颗小
}
}

具体操作

  • 讲了这么多,实际上与开头具体的操作关系并不大。现在,就看看如何用以上两个函数实现这些操作
  1. 插入

    先新建一个节点,再按这个点的值进行分裂。这时,其中一个子树中所有值都小于等于新建节点的值。依次合并回去就好了
mt19937 myrand(time(0));
int newnode(int val)
{
int u=++cnt;
tr[u].siz=1,tr[u].val=val,tr[u].l=tr[u].r=0,tr[u].rnd=myrand();
return u;
}
int u=newnode(x);//x是输入进来的权值
int lrt,rrt;
split(root,lrt,rrt,x);
root=merge(merge(lrt,u),rrt);//注意合并时都要给root重新赋值
  1. 删除

    按x-1、x、x+1分裂成3个树,中间那个树所有权值都是x了。但注意不能将整个树全部扔掉。直接将中间树根节点的左右儿子合并即可。
int lr,mr,rr;
split(root,lr,rr,x-1);split(rr,mr,rr,x+1);
mr=merge(tr[mr].l,tr[mr].r);
root=merge(merge(lr,mr),rr);
  1. 查找x的排名

    按x-1分裂,直接输出较小的树的siz即可
int lr,rr;
split(root,lr,rr,x-1);
printf("%d\n",tr[lr].siz+1);
root=merge(lr,rr);
  1. 查找排名为x的数

    根据二叉搜索树的性质直接找
int kth(int u,int k)
{
int lsiz=tr[ls].size;
if(lsiz+1==k) return u;
if(lsiz+1>k) return kth(ls,k);
else return kth(rs,k-lsiz-1);
}
printf("%d\n",tr[kth(root,x)].val);
  1. 求x的前驱

    仍然利用kth函数,将root按x-1分裂成两个树,再找较小子树中最大的数
int lr,rr;
split(root,lr,rr,x-1);
printf("%d\n",tr[kth(lr,tr[lr].siz)].val);
root=merge(lr,rr);
  1. 求x的后继

    几乎同上,按x+1分裂,找较大子树中最小的数
int lr,rr;
split(root,lr,rr,x+1);
printf("%d\n",tr[kth(rr,1)].val);
root=merge(lr,rr);
  • 以上,就是普通平衡树的全部操作。

P2042 [NOI2005] 维护数列

  • 题意:

    需要对一个连续数列维护以下操作:

    区间插入

    区间删除

    区间修改

    区间翻转

    区间求和

    求当前数列最大字段和
  • 普通平衡树是维护一个集合的信息,那平衡树能不能维护一个连续数列呢?答案是可以的
  • 普通平衡树与数列平衡树最大的区别在于split。
  • 由于普通平衡树维护的是集合,操作大都与值相关,因此是按值分裂的

    而数列平衡树维护的是连续的数列,因此不能按值分裂,必须按位置分裂

FHQ treap(无旋treap)的更多相关文章

  1. fhq Treap(无旋Treap)

    先吹一波fhq dalao,竟然和我一个姓,我真是给他丢脸. 昨天treap就搞了一下午,感觉自己弱爆了.然后今天上午又看了一个上午的无旋treap再次懵逼,我太弱了,orzorz. 所以写个博客防止 ...

  2. Luogu 3369 / BZOJ 3224 - 普通平衡树 - [无旋Treap]

    题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3224 https://www.luogu.org/problemnew/show/P3 ...

  3. HNOI2012 永无乡 无旋Treap

    题目描述 永无乡包含 nnn 座岛,编号从 111 到 nnn ,每座岛都有自己的独一无二的重要度,按照重要度可以将这 nnn 座岛排名,名次用 111 到 nnn 来表示.某些岛之间由巨大的桥连接, ...

  4. [转载]无旋treap:从单点到区间(例题 BZOJ1500&NOI2005 维护数列 )

    转自ZZH大佬,原文:http://www.cnblogs.com/LadyLex/p/7182631.html 1500: [NOI2005]维修数列 Time Limit: 10 Sec  Mem ...

  5. [转载]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)

    转载自ZZH大佬,原文:http://www.cnblogs.com/LadyLex/p/7182491.html 今天我们来学习一种新的数据结构:无旋treap.它和splay一样支持区间操作,和t ...

  6. [您有新的未分配科技点]无旋treap:从好奇到入门(例题:bzoj3224 普通平衡树)

    今天我们来学习一种新的数据结构:无旋treap.它和splay一样支持区间操作,和treap一样简单易懂,同时还支持可持久化. 无旋treap的节点定义和treap一样,都要同时满足树性质和堆性质,我 ...

  7. 【算法学习】Fhq-Treap(无旋Treap)

    Treap——大名鼎鼎的随机二叉查找树,以优异的性能和简单的实现在OIer们中广泛流传. 这篇blog介绍一种不需要旋转操作来维护的Treap,即无旋Treap,也称Fhq-Treap. 它的巧妙之处 ...

  8. 无旋treap的区间操作实现

    最近真的不爽...一道维修数列就做了我1上午+下午1h+1晚上+晚上1h+上午2h... 一道不错的自虐题... 由于这一片主要讲思想,代码我放这里了 不会无旋treap的童鞋可以进这里 呵呵... ...

  9. 无旋treap的简单思想以及模板

    因为学了treap,不想弃坑去学splay,终于理解了无旋treap... 好像普通treap没卵用...(再次大雾) 简单说一下思想免得以后忘记.普通treap因为带旋转操作似乎没卵用,而无旋tre ...

  10. [BZOJ3223]文艺平衡树 无旋Treap

    3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB Description 您需要写一种数据结构(可参考题目标题),来维护一个 ...

随机推荐

  1. 本地部署DeepSeek后,没有好看的交互界面怎么行!

    大家好,我是晓凡. 写在前面 在前面的文章中手摸手的带小伙伴们在本地部署了DeepSeek.但是,部署完之后,我们要与之交互,只能在命令行中进行. 忘记怎么部署的小伙伴,来这儿复习一下~ 命令行交互如 ...

  2. Linux 宝塔常用命令教程

    一.引言 在 Linux 服务器管理中,宝塔面板是一款非常实用的工具.然而,了解一些相关的命令可以让我们在特定情况下更高效地进行操作和管理.本文将为您介绍一些常用的 Linux 宝塔相关命令. 二.安 ...

  3. 小米13Pro一键ROOT秒杀全版本

    小米13p专属 通杀全版本 但是必须解开bl锁 小米13pro一键root使用方法: 解锁bl后,不要设置锁屏密码,有的话就取消掉,打开软件,点击安装驱动(管理员) 手机上打开usb调试和usb安装 ...

  4. 湖北电信创维E900-S机顶盒-精简系统装当贝桌面

    一.打开机顶盒进入本地配置,输入密码:6321,然后打开其他设置-管理应用程序,连续按遥控器方向右键5次左右,这时会出现[USB调试]并打开: 二.从电脑里下载好当贝市场(点击立即下载).当贝桌面(点 ...

  5. 凌晨三点救火实录:Java内存泄漏的七个神坑,你至少踩过三个!

    引子:那个让运维集体加班的夜晚 "凡哥!线上服务响应时间飙到10秒了!"凌晨1点,实习生小李的语音带着哭腔. 监控大屏上,JVM堆内存曲线像坐了火箭--刚扩容的16G内存,30分钟 ...

  6. Flink - [04] 窗口(Windows)

    题记部分 一.Flink中的窗口是什么 (1)一般真实的流都是无界的,怎样处理无界的数据? (2)可以把无限的数据流进行切分,得到有限的数据集进行处理 -- 也就是得到有界流 (3)窗口(Window ...

  7. Vuex:让状态管理不再头疼的“管家”

    如果你正在开发一个 Vue.js 应用程序,但发现自己被各种组件之间的状态共享问题搞得焦头烂额,那么 Vuex 就是你需要的"超级管家".Vuex 是专门为 Vue.js 设计的状 ...

  8. 「二」移动光标、vim进入与退出、文本编辑之删除、插入、添加、编辑、光标移动、撤销

    移动光标 h:向左移动 j:向下移动 k:向上移动 l:向右移动 vim进入与退出 按鍵, 确保处于正常模式 輸入:q! <回车>(丢弃所做的任何改动) 文本编辑之删除 在正常模式下修改命 ...

  9. springboot2.1.6整合activiti6.0(二)--网页流程编辑器bpmnjs

    网页流程编辑器bpmnjs 官网:https://bpmn.io/ github:https://github.com/bpmn-io/bpmn-js-examples 因为还需要做一些改造,才能使其 ...

  10. VScode美化

    RESULT:EVA-初号机 配色 主题效果 1. 需要的东西 vs code background 插件 custom CSS and JS loader 插件 一些png素材,推荐网址: http ...