可持久化数据结构

可持久化数据结构允许访问历史版本,如可持久化线段树和可持久化平衡树。线段树的可持久化通过在每次修改时创建节点副本实现,保持O(log n)的时间复杂度。而可持久化平衡树,如非旋转式Treap,通过Merge和Split操作保持平衡,其可持久化版本同样保持O(log n)的时间复杂度。这两种数据结构常用于区间查询和修改问题,如区间K大值问题。
摘要由CSDN通过智能技术生成

可持久化数据结构

毒液哥 Fudan University

概论

本文假设读者对数据结构有一定了解,对此概念不做赘述。

可持久化数据结构是一类数据结构的统称。若我们能在某一时刻访问一数据结构的任何历史版本,则称该数据结构为可持久化数据结构。
若一个数据结构是可持久化的,则可以通过修改该数据结构,在各操作时间复杂度不改变的同时,使其成为一个可持久化数据结构。

由定义可知,可持久化数据结构的任何一个历史版本都要被保留,因此可持久化数据结构的任何操作都不能直接修改之前存在的节点,必须对每个需要修改的节点创建一个拷贝。因此只有当任何时间的任何操作创建节点拷贝的复杂度都严格小于等于其复杂度,这一数据结构才能可持久化。

下文会讨论一些常见的数据结构的一般版本,及其可持久化版本,并分析一些不能可持久化的一般版本。


可持久化线段树

一般线段树

一般来说,线段树有两种实现方法,一种是基于数组的静态开点的线段树,另一种是使用指针储存树形结构的动态开点(也可静态开点)版本。其中前者本质上是对后者的一种优化。在线段树的坐标范围确定的情况下,整颗线段树的节点数量以及每个节点表示的线段其实是确定的。若将该线段树的结构完整建立,则它是一棵节点数不超过 O ( n ) O(n) O(n)的正则二叉树,其中 n n n为坐标范围的长度。因此可以用数组储存这棵二叉树,从而省去了记录左右儿子关系的空间。

因为线段树的每个操作会严格修改 O ( log ⁡   n ) O(\log\ n) O(log n)个节点,所以线段树可持久化。但由于前者用数组存储了整棵线段树,但拷贝数组需要 O ( n ) O(n) O(n)的时间,因此前一个版本的线段树不可持久化,而后一个版本的线段树可持久化。

可持久化

u u u表示一棵满节点线段树的一个节点,则:
l e f t ( u ) left(u) left(u)表示 u u u的左儿子; r i g h t ( u ) right(u) right(u)表示 u u u的右儿子; d a t a ( u ) data(u) data(u)表示 u u u上储存的数据; L ( u ) L(u) L(u)表示 u u u(线段)的左端点; R ( u ) R(u) R(u)表示 u u u(线段)的右端点。

考虑一般线段树的单点递归修改过程。
递归函数 F F F的参数 u ,   x ,   y u,\ x,\ y u, x, y分别表示线段树的节点、要修改的坐标、以及数据的修改量,没有返回值:

  1. 判断 u u u是否是叶子节点,若是,直接修改 d a t a ( u ) data(u) data(u)并返回。
  2. 判断 x x x位于 l e f t ( u ) left(u) left(u)还是 r i g h t ( u ) right(u) right(u). 不妨设是前者。
  3. 递归调用 F ( l e f t ( u ) ,   x ,   v ) F(left(u),\ x,\ v) F(left(u), x, v).
  4. l e f t ( u ) left(u) left(u) r i g h t ( u ) right(u) right(u)更新 d a t a ( u ) data(u) data(u).

我们发现,所有被访问到的节点的信息都会被修改,因此在可持久化版本中,我们应该对所有访问到的节点制作一份副本,并对副本进行修改。同时需要注意,在第三步中,由于我们为修改过的 l e f t ( u ) left(u) left(u)创建了一份副本,我们应该将新节点的左儿子指向修改过后的 l e f t ( u ) left(u) left(u)副本。下面给出可持久化线段树的单点递归修改过程。

递归函数 G G G的参数 u ,   x ,   y u,\ x,\ y u, x, y分别表示线段树的节点、要修改的坐标、以及数据的修改量,返回值为修改过后的 u u u的副本(即 v v v):

  1. 创建 u u u的一个拷贝,设其为 v v v.
  2. 判断 v v v是否是叶子节点,若是,直接修改 d a t a ( v ) data(v) data(v)并返回 v v v
  3. 判断 x x x位于 l e f t ( u ) left(u) left(u)还是 r i g h t ( u ) right(u) right(u). 不妨设是前者。
  4. 递归调用 G ( l e f t ( u ) ,   x ,   v ) G(left(u),\ x,\ v) G(left(u), x, v). 并令 l e f t ( v ) left(v) left(v)更新为其返回值。
  5. l e f t ( v ) left(v) left(v) r i g h t ( v ) right(v) right(v)更新 d a
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值