首页 > 其他分享 >平衡树学习笔记

平衡树学习笔记

时间:2023-05-02 19:00:42浏览次数:39  
标签:Splay phi log 复杂度 笔记 旋转 学习 平衡 节点

前置芝士

平衡树的前置芝士:全局平衡二叉树

平衡树

平衡树是一种基于二叉搜索树的数据结构。
满足:左儿子 \(<\) 根 \(<\) 右儿子。
也就是一切小于根节点的在左边,一切大于根节点的在右边。
这样想要查找一个节点的位置时间复杂度就是 \(O(\log n)\)。

平衡树主要有三种:Splay,Treap,无旋Treap。
还有其他的像替罪羊树,红黑色。

Splay

Splay 由于 LCT 滚出 OI 的原因也跟着少用了。
但是本人以为还是 Splay 比较好用的。

核心操作

Splay 的基本操作就是将BST旋转,分左旋和右旋(其实都差不多)
旋转的要求:在不改变原有的中序遍历的前提下改变树的结构。
简化一下:把儿子节点与父亲节点的身份互换,且有BST性质。

旋转 \(zig(x)\),\(zag(x)\) 如下图:
image

具体描述

设要旋转的节点为 \(x\),它的父亲为 \(y\),\(y\) 的父亲为 \(z\)。

  • 将 \(y\) 的左儿子设为 \(x\) 的右儿子
  • 若 \(x\) 的右儿子存在,将 \(x\) 的右儿子的父亲设为 \(y\)
  • 将 \(x\) 的右儿子设为 \(y\)
  • 将 \(y\) 的父亲设为 \(x\)
  • 将 \(x\) 的父亲设为 \(z\)
  • 若 \(z\) 存在,将 \(z\) 的某个子节点(原来 \(y\) 所在的子节点)设为 \(x\)

双旋操作

在 Splay 中,每加入一个新的节点就需要把它旋转到根。
设当前需旋转的节点为 \(x\),节点的旋转可分为以下三种:
\(1.\)\(x\) 的父亲是根,这时直接旋转即可。
\(2.\)父亲和 \(x\) 的儿子同侧(即同为左儿子或同为右儿子),这时先旋转父亲,再旋转 \(x\)。
\(3.\)父亲和 \(x\) 的儿子不同侧,这时将 \(x\) 旋转两次。
如下图:
image
image

时间复杂度

对于这个时间复杂度的分析,我们需要用一下势能分析
设\(siz(x)\) 表示以 \(x\) 为根节点的子树大小。
\(\phi(x)\) 表示在进行 \(x\) 次操作后的势能函数。
\(c(x)\) 表示时间的时间复杂度。
\(a(x)\) 表示均摊的时间复杂度。
所以根据定义,我们可以得出:
\(\phi(x)=\log(siz(x))\)
\(a(x)=c(x)+\phi(x)-\phi(x-1)\)
即 \(\sum a(x)=\phi(n)-\phi(0)+\sum c(x)\)
因为根据 \(\phi(x)=\log(siz(x))\),所以现在肯定有:\(|\phi(n)-\phi(0)|\le n\log n\)
考虑每一次的 \(\Delta\phi\)

对于zig:

\[\Delta\phi=\phi'(p)-\phi'(p)+\phi'(x)-\phi(x)=\phi'(p)-\phi(x)\le\phi'(x)-\phi(x) \]

image

对于zig-zig

\[\Delta\phi=\phi'(z)+\phi'(y)+\phi'(x)-\phi(z)-\phi(y)-\phi(x) \]

\[=\phi'(z)-\phi'(y)-\phi(y)-\phi(x) \]

\[\le\phi'(x)+\phi'(z)-2\phi(x) \]

\[\le 3\phi'(x)-3\phi(x)+(\phi(x)+\phi'(z)-2\phi'(x)) \]

\[\le 3(\phi'(x)-\phi(x))-2k \]

image

那么Splay到根的均摊代价为 \(O(logn)\),\(zig\) 只有一次操作,所以只会使均摊代价增加\(k\)
再算上 \(\phi(n)-\phi(0)=n\log n\)
所以总时间复杂度为 \(O(n\log n)\)。

操作

讲了核心操作和时间复杂度后,我们来看看它可以支持的操作。
注:由于我们只能保证 Splay 本身的时间复杂度,所以我们就必须只能通过旋转来实现一些操作。

查询数值

给定一个数 \(k\),查询排名为 \(k\) 的数。

  • 若 \(k\) 小于等于左子树大小,就向左子树走
  • 否则,将 \(k\) 先减去左子树大小和当前节点的 \(cnt\),使得 \(k\), 等于在右子树中的排名。然而若 \(k\) 小于等于 \(0\),说明已经找到,进行旋转,并返回当前节点权值。

查询 \(x\) 的前驱和后继

我们先将 \(x\) 节点旋转到根节点。

  • 前驱:就是 \(x\) 左子树中最右边的节点。
  • 后继:就是 \(x\) 右子树中最左边的节点。

删除节点 \(x\)

我们需要先将 \(x\) 旋转到根节点,从而去得到它的前驱和后继。
然后将前驱旋转到根,再将后继旋转到根,就会得到下图:
image
直接把 \(x\) 删去即可。

查询区间 \(\left[l,r\right]\)

我们需要将 \(l\) 的前驱旋转到根,再将 \(r\) 的后继旋转到根节点,点像删除操作。
而 \(l\) 前驱的右子树就是整个 \([l,r]\) 区间了。

标签:Splay,phi,log,复杂度,笔记,旋转,学习,平衡,节点
From: https://www.cnblogs.com/OIerBoy/p/17367976.html

相关文章

  • 机器学习算法 随机森林学习 之决策树
    随机森林是基于集体智慧的一个机器学习算法,也是目前最好的机器学习算法之一。随机森林实际是一堆决策树的组合(正如其名,树多了就是森林了)。在用于分类一个新变量时,相关的检测数据提交给构建好的每个分类树。每个树给出一个分类结果,最终选择被最多的分类树支持的分类结果。回归则是不......
  • 【pytorch】土堆pytorch教程学习(四)Transforms 的使用
    transforms在工具包torchvision下,用来对图像进行预处理:数据中心化、数据标准化、缩放、裁剪、旋转、翻转、填充、噪声添加、灰度变换、线性变换、仿射变换、亮度/饱和度/对比度变换等。transforms本质就是一个python文件,相当于一个工具箱,里面包含诸如Resize、ToTensor、Nor......
  • 迁移学习《mixup: Beyond Empirical Risk Minimization》
    论文信息论文标题:mixup:BeyondEmpiricalRiskMinimization论文作者:TakeruMiyato,S.Maeda,MasanoriKoyama,S.Ishii论文来源:2018ICLR论文地址:download 论文代码:download视屏讲解:click ......
  • render学习
    一.前言1.vue程序的运行过程:模板->进行编译->生成ast树->数据绑定->生成render函数->成虚拟dom树->真实dom树模板:Vue的模板基于纯HTML,基于Vue的模板语法,我们可以比较方便地声明数据和UI的关系。AST:AST是AbstractSyntaxTree的简称,俗称‘抽象语法树’它是一种......
  • 代码自测学习
    1.tensor索引[:,0:3,] 代表从0行开始,一共3-0行b=torch.arange(16,dtype=float).reshape(1,4,4)print(b)print(b[:,0:1,]) ......
  • Vue指令学习
    1.指令的定义指令(Directives)是带有 v- 前缀的特殊attribute。指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于DOM。指令还有一些基本的要了解的就是指令的修饰符(.native,.stop,.prevent等),动态参数(<a@[event]="doSomething">),缩写(:,@等)。这些都是......
  • 《重构:改善既有代码的设计》学习笔记
    代码的坏味道名称说明重构手法神秘命名MysteriousName好的命名能够节省时间改变函数神秘、变量改名、字段改名重复代码DuplicatedName重复代码让人不得不留意其中的细微差异提炼函数、移动语句、函数上移过长函数LongFunction函数越长,就越难理解提炼函......
  • 迁移学习(VMT)《Virtual Mixup Training for Unsupervised Domain Adaptation》
    论文信息论文标题:VirtualMixupTrainingforUnsupervisedDomainAdaptation论文作者:TakeruMiyato,S.Maeda,MasanoriKoyama,S.Ishii论文来源:2019CVPR论文地址:download 论文代码:download视屏讲解:click   ......
  • 点分治学习笔记
    点分治序列上的操作可以用很多方式维护,线段树树状数组平衡树啥的,但是如果毒瘤出题人把序列搬到了树上,则需要一些奇妙方法。一般有两种(好几种?)方法:树链剖分,把树上路径转化成dfn序上的多段进行操作。LCT,不多说,目前只会板子,没搞太懂。点分治,这个是不用把树转化成序列的,而是将树......
  • Qt 学习笔记
     1.  *newClass 与引用<qpushbutton.cpp>:QPushButton::QPushButton(QWidget*parent):QAbstractButton(*newQPushButtonPrivate,parent){Q_D(QPushButton);d->init();}<qabstractbutton.cpp>:/*!\internal*/对应的函数原型......