Lec 4伸展树与自调整二叉树
Lec 4 伸展树与自调整二叉搜索树(Splay Trees & Self-Adjusting Search Trees)
对应核心文献:Sleator, D. D., & Tarjan, R. E. (1985). Self-adjusting binary search trees. Journal of the ACM.
TL;DR
- 自调整哲学与零额外开销:完全摒弃 AVL 树与红黑树所需的平衡因子或颜色位,通过每次访问时自发将目标节点向上旋转(Splaying)至根部,实现均摊 查找。
- Zig-Zig 双旋转折半长链:伸展操作在同侧祖先场景下坚持“先旋父节点再旋子节点”,从根本上将退化长链路径长度减半,克服了朴素单旋转反复退化为长链的缺陷。
- 静态最优性与动态最优性猜想:伸展树的势能分析自然蕴含熵界,能自适应局部热点访问达到信息论最优;其动态最优性猜想是算法理论界四十年来的最高未解之谜之一。
自调整数据结构的设计动机与哲学
在传统平衡二叉搜索树(如 AVL 树、红黑树、Treap)中,维持平衡需要付出清晰的代价:
- 显式元数据开销:每个节点必须存储高度差(平衡因子)或颜色位,占用内存字长;
- 严苛的最坏情况维护:每次插入或删除后,必须沿着回溯路径执行复杂的条件分支判断与旋转,代码实现繁复;
- 无法利用访问的局部性(Locality of Reference):在现实系统中,数据的访问分布往往极不均匀(例如 80% 的请求集中于 20% 的热点数据)。传统平衡树对任何节点的查找时间恒为 ,无法利用频繁访问热点来压低实际延迟。
Sleator 与 Tarjan(1985)提出了自调整二叉搜索树(Self-Adjusting Binary Search Tree,即伸展树 Splay Tree): 树中不存储任何平衡元数据,也不保证任何单次操作的最坏情况树高,但在每次访问某个节点后,通过一系列精心编排的旋转将其推升至树根。这一自发的局部调整足以在均摊意义下保证最优性能,并自动将热点数据吸附至根部浅层。
伸展操作(Splaying)与核心旋转规则
朴素单旋转的陷阱
在伸展树诞生前,研究者曾尝试使用朴素的单旋转(Move-to-root by simple rotations)将访问的节点一步步旋转到根节点。
然而,如果初始树退化为一条长为 的单向链表,连续访问最深处的叶节点:单次单旋转虽然将该叶子提升至树根,但原链表上的其他节点仅仅被翻转并横向挂载,整棵树并未被实质性压扁。连续按特定逆序访问节点会导致每次访问均消耗 ,使得总均摊复杂度恶化为 。
伸展操作的三种旋转步
设当前待提升的节点为 , 的父节点为 , 的父节点( 的祖父节点)为 。伸展操作重复执行以下步骤,直至 成为整棵树的根节点:
1. Zig 步(单旋转,终止步)
- 触发条件: 已经是整棵树的根节点( 没有祖父节点 )。
- 动作:对边 执行一次标准的单旋转(左旋或右旋),使 成为树根。
2. Zig-Zig 步(同向双旋转,压平核心)
- 触发条件: 与 位于同侧( 是 的左孩子且 是 的左孩子;或者 是 的右孩子且 是 的右孩子)。
- 关键动作:先旋转父节点 关于祖父节点 ,再旋转节点 关于父节点 !
- 重要几何效果:这一反直觉的旋转顺序直接将从 到 的长路径折叠,使路径上所有节点的深度大致减半,从而真正压扁了树的形态。
g p x
/ \ / \ / \
p D ===> x g ===> A p
/ \ / \ / \ / \
x C A B C D B g
/ \ / \
A B C D
3. Zig-Zag 步(异向双旋转)
- 触发条件: 与 位于异侧(例如 是 的右孩子,而 是 的左孩子)。
- 动作:先旋转 关于 (将其转化为同侧关系),再旋转 关于 。这与 AVL 树的标准双旋转完全一致。
访问引理(Access Lemma)与均摊分析
伸展树的优雅完全建立在其势能分析之上。
权重、子树和与秩(Rank)的定义
为整棵树中的每个节点 赋予一个任意的正实数权重 :
- 加权子树规模:
- 节点的秩(Rank):
- 全局势能函数:
注意:这里的权重 纯粹是均摊分析的数学工具,伸展树的数据结构代码中根本不需要存储和维护权重!
访问引理(Access Lemma)
引理:对根为 的伸展树中的任意节点 执行伸展操作,其均摊时间满足:
其中 为伸展开始前 的秩, 为伸展完成后根节点的秩。
核心数学不等式(对数的凹性)
对于任意正数 ,若 ,则根据对数函数的凹性:
关键情形证明概要
设单步旋转前各节点的秩为 ,旋转后各节点的秩为 。单次双旋转的实际耗时记为 2。
-
对于 Zig-Zag 步: 旋转后 取代了 的位置,故 。且显然 。 势能变化为 。 利用凹性不等式:。 代入得均摊代价:
-
对于 Zig-Zig 步: 旋转后 ,且有 。 类似地可推导出:
将伸展操作路径上的所有 Zig-Zig 和 Zig-Zag 步的均摊代价累加,中间项裂项相消(Telescoping),最后加上至多一步 Zig 步(代价 ),即完成访问引理的证明。
伸展树的优越性质与定理集群
通过为节点赋予不同的数学权重分配,访问引理能够推导出一系列惊人的理论性质:
1. 平衡定理(Balance Theorem)
令所有节点权重恒为 。 则总权重 ,。由于对任意节点 。 单次伸展均摊时间满足:
这证明了:没有任何平衡因子的伸展树,在均摊意义下完全能够达到标准平衡二叉树的 性能。
2. 静态最优性定理(Static Optimality Theorem)
设每个元素 的访问频次(或概率)为 。为每个节点赋予权重 。 单次访问元素 的均摊代价为:
对包含 次访问的序列,总耗时为:
其中 是访问分布的信息熵(Entropy)! 这表明:伸展树在任意访问序列上的表现,不劣于由已知概率预先静态构建的最优二叉搜索树(Optimal BST),且伸展树是在完全不知道概率分布的情况下在线自发达成的!
3. 工作集定理(Working Set Theorem)
每次访问元素 时,设自 上一次被访问以来,其间被访问的不同元素总数为 。 伸展树执行本次访问的均摊代价为:
这意味着,如果程序维护一个局部热点工作集,伸展树的访问开销只取决于工作集的大小,而不是整棵树的大小 。
4. 动态指引定理(Dynamic Finger Theorem)
若连续访问两个键值分别为 和 的节点,伸展树的均摊访问代价仅为:
在有序扫描或局部跳跃访问时,伸展树几乎展现出双向链表般的近常数性能。
动态最优性猜想(Dynamic Optimality Conjecture)
在所有关于自调整二叉树的理论探讨中,最著名的莫过于 Sleator 与 Tarjan 在 1985 年提出的动态最优性猜想:
猜想内容:是否存在一个全局常数 ,使得对于任意给定的访问序列 ,在线自调整的伸展树所花费的总代价,不超过任何能够预知未来的最优离线自调整二叉搜索树算法(Offline Optimal BST)所需代价的 倍?
该猜想等价于断言:伸展树是一个 -竞争比(Competitive Ratio)的在线算法。 尽管四十年来数学家与理论计算机科学家(包括 Demaine 等人提出的 Tango Tree 达到 竞争比)取得了重大进展,但关于原始伸展树是否具备 动态最优性,至今仍是理论计算机科学界最著名的未解猜想之一。
基本集合操作的伸展实现
借助 Splay(x) 操作,二叉搜索树的所有常用操作都变得极其优雅统一:
Find(x):标准 BST 查找。找到节点后执行Splay(x)移至根;若未找到,则对访问路径上的最后一个节点执行伸展。Insert(x):先查找 并伸展。此时树根为 的前驱或后继。以树根为界将树断开,将 作为新根插入。Delete(x):查找并执行Splay(x)将其提升到树根。将其左右子树断开;在左子树中查找最大元素并伸展(此时该最大元素无右孩子),将原右子树直接挂接为其右子树。Split(x)/Join(T1, T2):伸展天然支持在 均摊时间内将树按键分裂或合并。
我的理解
我的理解
自调整是将“静态结构约束”升华为“动态时序适应”: AVL 树和红黑树代表了经典的结构力学思维:通过在每个节点砌筑刚性的几何约束(高度差 、黑高度恒定),确保任何时候都四平八稳。但这种刚性无法感知外界访问的温度,即使同一个热点被访问一万次,它依然深埋在树底。
伸展树则展现了热力学式的生命力:彻底放弃对瞬时形态的执念,允许在对抗性序列下暂时退化为长链。但关键在于 Zig-Zig 步中“先旋父再旋子”的精妙几何重组,使得每一次对深渊的探寻都伴随着对整条路径的“压平改造”。热点频繁浮动于根部,冷点退居边缘。它不靠外在的平衡指标,而仅仅依靠与访问流的共舞,就自发逼近了信息论的熵界。