- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
平衡树(一)链接: 算法学习笔记(18): 平衡树(一) - jeefy - 博客园 。
本文中将讲述一下内容:
可持久化Treap 。
基于 Trie 的 类 平衡树(后文称之为 BSTrie ) 。
BSTrie的可持久化 。
可持久化Treap基于FHQ-Treap。其实不难发现,FHQ-Treap在分裂和合并时在每一层只对一个结点产生影响。于是我们可以大胆的可持久化此结构,且保证复杂度为 \(O(\log n)\) .
我们以此图为例。我们只需要复制下被影响的结点,基于原树获得一条新链:
红色节点是新产生的节点(可能实际上产生的顺序不一样) 。
其中 (8, 11) 作为新右树的根, (7, 8) 作为新左树的根 。
也就是说,我们经过一个结点复制一次即可.
inline void splitVal(int p, int val, int &x, int &y, bool simple = true) {
if (!p) return (void)(x = y = 0);
int np = simple ? p : clone(p);
if (val(p) <= val)
x = np, splitVal(rp(p), val, rp(x), y, simple), update(x);
else
y = np, splitVal(lp(p), val, x, lp(y), simple), update(y);
}
simple就是标志是否需要可持久化……特别简单 。
其实到这里就已经讲完了可持久化Treap了……因为其他绝大部分操作只需要对于分裂时持久化,在合并时并不需要持久化.
例如删除操作:
inline int insert(int root, int val, bool simple = false) {
int x(0), y(0), p(++usage);
nodes[p].init(val);
splitVal(root, val, x, y, simple);
return merge(merge(x, p), y);
}
这也请读者自行思考为什么不需要合并时可持久化.
这里基于的Trie指的是 \(01Trie\) ,考虑其实每一个数都可以被拆分为二进制,于是有了此做法.
说实话,代码无比之简短,并且十分迅速,除了空间复杂度较大之外,令我不禁想要抛弃WBLT…… 。
后记:空间复杂度……真的很大,很多普通平衡树能过的空间,Trie真不一定能过 。
我们首先只考虑正数的情况 。如果我们把每一个数都扩展成同一个位长,从高位向低位保存成一棵树。我们从左到右(认为0在左,而1在右)观察其叶节点所对应的值(类似于Leafy Tree),可以知道是单调递增的,于是我们可以轻易的将之进行魔改,从而做到普通平衡树所能做到的事.
template<int N = 100000>
class BSTrie {
private:
int siz[N << 5];
int ch[N << 5][2];
int usage;
#define newNode() ({ \
++usage; \
siz[usage] = ch[usage][0] = ch[usage][1] = 0; \
usage; \
})
}
这是这一颗树需要用到的内容。其实从这里就应该可以知道,其空间复杂度为 \(O(n \log C)\) 其中 \(C\) 表示值域大小,一般为 \(32\) 。这与其他空间为 \(O(n)\) 的平衡树相比远远不如…… 。
首先看代码:
void insert(int x) {
int p = 1;
for (int i = BS; ~i; --i) {
int bt = bit(x, i), &np = ch[p][bt];
if (!np) np = newNode();
p = np, ++siz[p];
}
}
写法有些许迷惑,见谅 。
其中BS指的是 \(\lceil \log C \rceil\) 。
由于我们需要用到子树的大小以方便 \(rank, kth\) 操作,所以对于路径上 ++siz[p] 。
不就是普通的 Trie 插入操作吗?不讲了.
void remove(int x) {
int p = 1;
for (int i = BS; ~i; --i) {
int np = ch[p][bit(x, i)];
if (!np) return;
p = np;
}
p = 1;
for (int i = BS; ~i; --i) {
p = ch[p][bit(x, i)], --siz[p];
}
}
这里需要注意的是需要两遍向下,以防止 x 并不存在的情况.
与普通的 Trie 删除操作类似,想必代码也十分易懂.
在普通平衡树内查询排名怎么查,这里就怎么查:
进入右子节点,则累加左子树的大小 。
进入左子节点,则不累加 。
没有子节点,直接返回当前结果 。
int rank(int x, bool within = false) {
int p = 1;
int rk = 0;
if (x >= 0) rk += siz[1];
for (int i = BS; ~i; --i) {
int bt = bit(x, i), np = ch[p][bt];
if (bt) rk += siz[ch[p][0]];
if (!np) return rk;
p = np;
}
return within ? rk + siz[p] : rk;
}
这里对于加入了 within 参数,用于提示是否需要包含 x 出现的次数.
为什么在第8行直接返回是正确的?简单来说就是空子树不会对结果造成影响。具体一点请读者自行思考.
呃,令 x 为当前结果:
若进入左子树,则令 x = x << 1 。
否则令 x = (x << 1) | 1 (打括号是为了方便理解) 。
如果保证树内存在至少 \(k\) 个数,则一定可以找到正确答案,且不会进入空子树.
但是没有这么多个数……则结果不可预测 。
int kth(int k) {
int p = 1;
int x = 0;
for (int i = BS; ~i; --i) {
if (k <= siz[lc(p)]) p = lc(p), x <<= 1;
else k -= siz[lc(p)], p = rc(p), x = (x << 1) | 1;
}
return x;
}
于是,你可以在100行内写出一个优秀的平衡树了…… 。
现在考虑有复数的情况。有两种解决方法:
依据符号位,建两棵树。根据补码的知识,对于有符号类型的整数,其对应的无符号整型数值越大,其值越大。所以负数也可以利用同样的代码处理.
而改变方法也很简单,将语句 int p = 1 改为 int p = x < 0 ? 1 : 2 (标号随意)即可。 只是在 kth 时,需要有: int x = k <= siz[1] ? (1 << (32 - BS)) - 1 : 0,
在 rank 前要多加一句: if (x >= 0) rk += siz[1],
其他的都没大区别.
第二种方案相对简单,把所有数都加上一个 offset ,保证为正整数即可……(这种方法很简答,而且可持久化时也更简单) 。
于是我们优先采用第二种方法(尤其是需要可持久化的时候).
可持久化 Trie 会吧……OK,下课! 。
还是提一下可持久化的思想:
把每一个经过的结点复制出来即可……类比可持久化Treap的操作 。
最后此篇关于算法学习笔记(21):平衡树(二)的文章就讲到这里了,如果你想了解更多关于算法学习笔记(21):平衡树(二)的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
OkHttp的作用 OkHttp is an HTTP client。 如果是HTTP的方式想得到数据,就需要我们在页面上输入网址,如果网址没有问题,就有可能返回对应的String字符串,如果这个地址
Record 一个重要的字符串算法,这是第三次复习。 通过总结我认为之所以某个算法总是忘记,是因为大脑始终没有认可这种算法的逻辑(也就是脑回路)。 本篇主要讲解从KMP的应用场景,
SQL 注入基础 【若本文有问题请指正】 有回显 回显正常 基本步骤 1. 判断注入类型 数字型 or 字符型 数字型【示例】:
标签: #Prompt #LLM 创建时间:2023-04-28 17:05:45 链接: 课程(含JupyterNotebook) , 中文版 讲师: An
Swift是供iOS和OS X应用编程的新编程语言,基于C和Objective-C,而却没有C的一些兼容约束。Swift采用了安全的编程模式和添加现代的功能来是的编程更加简单、灵活和有趣。界面则基于
红日靶机(一)笔记 概述 域渗透靶机,可以练习对域渗透的一些知识,主要还是要熟悉 powershell 语法,powershell 往往比 cmd 的命令行更加强大,而很多渗透开源的脚本都是 po
八大绩效域详细解析 18.1 干系人绩效域 跟干系人所有相关的活动. 一、预期目标 ①与干系人建立高效的工作关系 ②干系人认同项目目标 ③支持项目的干系人提高
18.3 开发方法和生命周期绩效域 跟开发方法,项目交付节奏和生命周期相关的活动和职能. 一、预期目标: ①开发方法与项目可交付物相符合; ②将项目交付与干系人价值紧密
18.7 度量绩效域 度量绩效域涉及评估项目绩效和采取应对措施相关的活动和职能度量是评估项目绩效,并采取适当的应对措施,以保持最佳项目绩效的过程。 一、 预期目标: ①对项目状况
pygraphviz 安装,windows系统: 正确的安装姿势: Prebuilt-Binaries/PyGraphviz at master · CristiFati/Prebuilt-Binar
今天给大家介绍IDEA开发工具如何配置devtools热加载工具。 1、devtools原理介绍 spring-boot-devtools是spring为开发者提供的热加载
一 什么是正则表达式 // 正则表达式(regular expression)是一个描述字符模式的对象; // JS定义RegExp类表示正则表达式; // String和RegExp都定义了使用
目前是2022-04-25 23:48:03,此篇博文分享到互联网上估计是1-2个月后的事了,此时的OpenCV3最新版是3.4.16 这里前提是gcc,g++,cmake都需要安装好。 没安装好的,
一、概述 1、Flink 是什么 Apache Flink is a framework and distributed processing engine for stateful comput
一、window 概述 Flink 通常处理流式、无限数据集的计算引擎,窗口是一种把无限流式数据集切割成有限的数据集进行计算。window窗口在Flink中极其重要。 二、window 类型 w
一、触发器(Trigger) 1.1、案例一 利用global window + trigger 计算单词出现三次统计一次(有点像CountWindow) 某台虚拟机或者mac 终端输入:nc -
一、时间语义 在Flink 中涉及到三个重要时间概念:EventTime、IngestionTime、ProcessingTime。 1.1、EventTime EventTime 表示日志事
一、概述 以wordcount为例,为什么每次输入数据,flink都能统计每个单词的总数呢?我们都没有显示保存每个单词的状态值,但是每来一条数据,都能计算单词的总数。事实上,flink在底层维护了每
一、概述 checkpoint机制是Flink可靠性的基石,可以保证Flink集群在某个算子因为某些原因(如 异常退出)出现故障时,能够将整个应用流图的状态恢复到故障之前的某一状态,保 证应用流图状
一、standalone 部署模式 1、下载安装包 下载安装包地址 有两种安装包类型: 第一种是带 Hadoop依赖的(整合YARN) 第二种是不带 Hadoop依赖的(Standalone模式)
我是一名优秀的程序员,十分优秀!