(a3)伸展树:算法实现

官方信息技术老师·10 页·深入(追求细节与边界)·0 次浏览·3 天前
伸展树旋转策略均摊分析

(a3)伸展树:算法实现

看清 splay 旋转细节与边界,掌握均摊 O(log n) 的记账分析

按 空格/→ 演示下一步

1 / 10 页

全部页面点击任意一页,跳回舞台从这页播放

伸展树旋转策略均摊分析

(a3)伸展树:算法实现

看清 splay 旋转细节与边界,掌握均摊 O(log n) 的记账分析

1第 1 页 · (a3)伸展树:算法实现

功能接口

BST 你会用——Search、Insert、Delete。伸展树对外暴露的接口和它一模一样,签名都不变。区别藏在每次写操作之后多走的那一步。

查询/插入/删除
签名与BST完全一致,调用方不感知底层换成伸展树
Splay(x)
把x沿访问路径旋转到根,是所有写操作的收尾动作
节点字段最简
只存key/left/right/parent,无height、无color
摊还复杂度
m次操作总代价O(m log n),单次最坏可退化到O(n)
把刚用过的工具归位到手边对应 →Splay把刚访问的节点挪到根

押注时间局部性——高频节点天然浮到浅层

$$\text{摊还代价}(op)=O(\log n),\quad \text{单次最坏}=O(n)$$
2第 2 页 · 功能接口

伸展算法

上一节看到了 splay 的接口:传入一个节点,它会被搬到树根。但「怎么搬」才是伸展树最巧妙的部分——每一步旋转的招式,取决于被搬节点在树中的位置。

伸展
访问某节点后,通过若干次旋转把它移到根位置
zig 单旋
被搬节点的父就是根,做一次普通旋转收尾
zig-zig
节点与父亲同为左孩子或同为右孩子,先旋父亲再旋自己
zig-zag
节点与父亲方向相反,先旋自己再旋父亲
整理书桌对应 →伸展树摊还思想

刚用过的资料挪到桌面最顺手处;下次再拿它直接就到

3第 3 页 · 伸展算法

四种情况

上一节要把 x 一路推到根。每一步看 x、父亲 p、祖父 g 三层关系,可归为四种对称情况;若 p 已是根节点则只做一次单旋收尾。

LL 一字型
三层同左:先右旋祖父 g,再右旋父亲 p
RR 一字型
三层同右:先左旋祖父 g,再左旋父亲 p
LR 之字型
p 在 g 右、x 在 p 左:先左旋 p,再右旋 g
RL 之字型
p 在 g 左、x 在 p 右:先右旋 p,再左旋 g
上楼的三阶路径形状对应 →一字型与之字型路径

三层同向是一字型直梯(先动远的祖父),异向是之字型拐角(先动近的父亲)

4第 4 页 · 四种情况

查找算法

前页拆解了四种旋转情况,现在把它们串成完整流程——查找路线和普通 BST 没有差别,真正的差别在「找到之后」还要做一步。

BST 基础查找
从根出发比大小,小往左、大往右,沿唯一路径下沉到目标
命中后伸展
找到目标节点后,以它为起点做 splay,把它旋到根
未命中伸展父节点
走到空也没找到时,把最后访问的那个节点旋到根(也是后续插入位置的父节点)
均摊 O(log n)
单次最坏 O(n),但任意 m 次连续操作均摊为 O(log n)
局部性副作用
刚访问的节点到了根,短时间内再访问几乎 O(1),搜索悄悄重塑了树
厨房找调料,用完顺手放到灶台边对应 →伸展树的「查找+伸展」

不是为了这一次省事,而是让后面很多次拿取都快起来

5第 5 页 · 查找算法

插入算法

上页讲到查找算法——找到就把节点旋到根。那如果 key 不存在呢?这就到了插入登场的时候。

按 BST 规则插入
从根出发比大小,沿左/右找到空位,把新节点挂上去
新节点必为叶子
插入后一定是叶子,没有左/右孩子,BST 部分到此结束
立即对新节点做伸展
调用前面的 splay,沿路径执行 zig / zig-zig / zig-zag
伸展后升为根
splay 结束时新节点成为树根,下次访问该 key 为 O(1)
摊还 O(log n)
BST 插入 O(h) + splay O(h),但摊还仍是 O(log n)
新书上架对应 →伸展树插入

按分类号归位(BST 插入),再把复本放前台(splay 到根),方便借阅

6第 6 页 · 插入算法

删除算法

插入时我们靠「先查找再伸展」把节点带到根,删除同样能享受这个便利——目标节点一旦被抬到根,后续就只剩拼接两棵子树,无需讨论左右孩子是否齐备。

查找即定位
先执行查找,路径上的伸展操作自动把目标节点抬到根节点
左右子树分割
以根为界拆成 L(全部更小)和 R(全部更大)两棵独立子树
合并子树
对 L 做一次伸展,把最大节点抬到 L 的根,再把 R 接为它的右孩子
零分支讨论
无需分孩子节点个数;最大节点的右孩子必空,挂 R 即可
整理一排按序排列的卡片对应 →删除目标节点

把要抽的卡片翻到最前(伸展到根),左右分两摞,再把右摞塞到左摞最大卡片后面

7第 7 页 · 删除算法

综合评价

前几页把伸展、查找、插入、删除都拆开了。现在把它们装回去看整体——伸展树到底是一种什么样的数据结构?

均摊 O(log n)
单次最坏 O(n),但任意连续 m 次操作均摊为 O(log n)
访问局部性
刚被访问的节点旋到根,热点数据自动靠近
实现极简
无需平衡因子或颜色位,只靠 splay 一个操作维持
不存额外信息
节点只有关键字和左右孩子,比 AVL、红黑树省空间
把最近用过的书移到书架最顺手处对应 →伸展树的访问局部性

单次搬书费力,但下次拿就快了——和单次 O(n)、均摊 O(log n) 一个道理

8第 8 页 · 综合评价

本节要点

  • 「访问即旋转」:把刚用过的节点提到根,形成热点加速
  • 摊还 O(log n) 由四种情况共同保证,缺一即退化为 O(n)
  • 增删查遵循同一套路:先伸展、再操作、再伸展
  • 摊还 ≠ 最坏:刻意构造序列仍可让单次走到 O(n)
  • 无平衡字段:靠旋转自维护,调试需打印路径观察树形
延伸主题:势能函数证明摊还界红黑树工程选型对比Finger Tree 等伸展变种
9第 9 页 · 本节要点

课后思考

先独立思考,再对照参考答案。三个问题层层递进:从机制到应用,再到理论边界。

1前面讲的四种旋转各自处理什么路径形态?为什么这样组合就能保证均摊 O(log n)?

参考答案zig 是父为根的最后一步;zig-zig/zig-zag 分别处理祖父-父-子同向与反向路径。势能分析证明:被访问节点在 splay 中势能下降,抵消路径代价,均摊 O(log n)。

2若访问集中在少数热点节点,伸展树相比 AVL、红黑树优势在哪?什么场景反而吃亏?

参考答案热点被反复伸展到根,局部性极佳;但单次最坏 O(n)、均摊常数大于严格平衡树——延迟敏感、完全随机的访问模式不适合。

3能否改造经典伸展树使单次操作最坏 O(log n)?代价是什么?

参考答案经典伸展树做不到。已有「严格伸展树」等变体可实现最坏 O(log n),但常数显著增大、实现更复杂,仅在可预知访问模式时值得。

10第 10 页 · 课后思考