C++ 伸展树与红黑树原理及实现
在平衡二叉搜索树(BBST)的家族中,伸展树(Splay Tree)和红黑树(Red-Black Tree)是两类极具代表性的结构。它们各自通过不同的策略来维护树的平衡性,以适应不同的应用场景。
1. 伸展树介绍
伸展树是一种自调整数据结构,由 John Edward Hopcroft 和 Robert Endre Tarjan 于 1985 年提出。与 AVL 树不同,伸展树不追求严格的平衡,而是利用'局部性原理',将最近访问的节点通过旋转操作移至根节点附近。
1.1 核心思想
当访问某个节点时,执行一次'伸展'(Splaying)操作,将该节点移动到根位置。这基于两个观察:
- 单一旋转:频繁访问的节点应靠近根部,以减少后续访问代价。
- 移动到根部:假设当前访问的节点会再次被高频访问,将其置于根节点可最大化效率。
1.2 旋转类型
一次伸展操作通常包含一系列双旋转或单旋转,旨在将目标节点提升至更高层级:
-
情况 1:单旋转 当被访问节点的父节点是根节点时,直接进行单旋转。此时被访问节点成为新的根。
-
情况 2:一字形旋转(Zig-Zig) 当被访问节点是其父节点的左子女,且父节点又是祖父节点的左子女(或同为右子女)时,执行双旋转。先围绕父节点旋转祖父节点,再围绕被访问节点旋转父节点。
-
情况 3:之字形旋转(Zig-Zag) 当被访问节点与其父节点、祖父节点形成折线形状(如左 -右或右-左)时,执行类似 AVL 树的双旋转。先围绕子节点旋转父节点,再围绕子节点旋转祖父节点。
1.3 算法描述
void splaying(Node* g, Node* p, Node* s) {
// g 是 p 的父结点,p 是 s 的父结点。算法将 s 移到根结点位置
while (s != root) {
if (s->parent == root) {
// 情况 1:单旋转
rotate(s);
} else if ((s->parent->left == s && s->parent->parent->left == s->parent) ||
(s->parent->right == s && s->parent->parent->right == s->parent)) {
// 情况 2:同构形状,一字形双旋转
rotate(p); rotate(s);
} else {
// 情况 3:异构形状,之字形双旋转
rotate(s); rotate(s);
}
}
}
虽然单次操作可能耗时 O(n),但在 m >= n 次操作的序列中,总时间复杂度为 O(m log n),均摊复杂度为 O(log n)。这使得伸展树非常适合缓存、热点数据查询等场景。
2. 红黑树介绍
红黑树是一棵二叉搜索树,每个节点增加一个颜色位(红色或黑色)。通过对路径上颜色的约束,确保没有一条路径比其他路径长出两倍,从而实现近似平衡。


