C++ 伸展树与红黑树详解
1. 伸展树介绍
伸展树(Splay Tree)是一种自调整的二叉搜索树,由 John Edward Hopcroft 和 Robert Endre Tarjan 于 1985 年提出。与 AVL 树不同,伸展树不追求严格的平衡,而是通过'伸展'操作将最近访问的节点移至根节点,利用局部性原理优化频繁访问的场景。
核心思想
- 单一旋转:将经常访问的节点上移,使其靠近根部。
- 移动到根部:假设节点会被高频再次访问,反复进行子女 - 父结点旋转,直到该节点位于根。
伸展操作类型
每次访问(搜索、插入或删除)一个节点时,执行一次'展开'过程,包含三种旋转:
- 单旋转 (Zig):被访问节点的父结点是根结点时执行。
- 一字形旋转 (Zig-Zig):被访问节点与其父结点、祖父结点同构(如左 -左或右 -右)时执行双旋转。
- 之字形旋转 (Zig-Zag):被访问节点与其父结点、祖父结点异构(如左 -右或右 -左)时执行双旋转。
void splaying(Node* g, Node* p, Node* s) {
// g 是 p 的父结点,p 是 s 的父结点。算法将 s 移到根结点位置
while (s != root) {
if (s->parent == root) {
// 情况 1: 单旋转
rotate(s->parent);
} else if ((s == s->parent->left) == (s->parent == s->parent->parent->left)) {
// 情况 2: 一字形双旋转
rotate(s->parent->parent);
rotate(s->parent);
} else {
// 情况 3: 之字形双旋转
rotate(s->parent);
rotate(s->parent->parent);
}
}
}
虽然单次操作可能耗时 O(n),但在 m >= n 次操作的序列中,总时间复杂度为 O(m log n),均摊后每次操作为 O(log n)。
2. 红黑树介绍
2.1 概念
红黑树是一棵二叉搜索树,每个节点增加颜色属性(红或黑)。通过对路径颜色的约束,确保没有一条路径比其他路径长出两倍,从而实现近似平衡。
2.2 性质
- 根结点和所有外部结点(NIL)的颜色是黑色。
- 从根到叶子的路径上,没有连续两个红色结点。
- 所有从根到外部结点的路径上都有相同数目的黑色结点(黑高度)。
2.3 效率分析
设根结点的黑高度为 r,则树的高度 h <= 2r。由于内部结点数量 n >= 2^r - 1,推导出 h <= 2 log₂(n+1)。因此,红黑树的增删查改最坏时间复杂度均为 O(log n)。相比 AVL 树,红黑树在插入时的旋转次数更少,维护成本更低,是 C++ STL 中 std::map、std::set 等容器的底层实现。


