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


