C++ 伸展树与红黑树原理及实现
1. 伸展树介绍
伸展树(Splay Tree)是一种自调整的二叉搜索树,由 John Edward Hopcroft 和 Robert Endre Tarjan 于 1985 年提出。与 AVL 树不同,伸展树不追求严格的平衡,而是利用'局部性原理',将最近访问的节点通过旋转操作移至根节点。
这种策略使得频繁访问的数据始终靠近根部,从而在均摊意义上达到 O(log n) 的时间复杂度。虽然单次操作可能耗时 O(n),但在 m >= n 次操作的序列中,总时间复杂度为 O(m log n)。伸展树非常适合缓存、热点数据查询等场景。
1.1 伸展操作
每当访问(搜索、插入或删除)一个节点 s 时,执行一次'伸展'过程,将其移至根节点。如果删除节点 s,则将其父节点伸展至根。伸展操作包含三种旋转类型:
- 单旋转:当被访问节点的父节点是根节点时执行。保持二叉搜索树特性,s 成为新根。
- 一字形旋转(Zig-Zig):当 s 与其父节点 p、祖父节点 g 同构(如左 -左或右 -右)时执行。先围绕 p 旋转 g,再围绕 s 旋转 p。
- 之字形旋转(Zig-Zag):当 s 与 p、g 异构(如左 -右或右 -左)时执行。先围绕 s 旋转 p,再围绕 s 旋转 g。
之字形旋转有助于减少树的高度,而一字形旋转主要提升访问节点的位置。
// 伪代码逻辑示意
void splaying(Node* s) {
while (s->parent != nullptr) {
Node* p = s->parent;
if (p->parent == nullptr) {
// 情况 1: 单旋转
rotate(p);
} else {
Node* g = p->parent;
if ((p->left == s && g->left == p) || (p->right == s && g->right == p)) {
// 情况 2: 一字形双旋转
rotate(g); rotate(p);
} else {
// 情况 3: 之字形双旋转
rotate(s); rotate(g);
}
}
}
}
2. 红黑树介绍
2.1 红黑树的概念
红黑树是一棵二叉搜索树,每个节点增加一个颜色位(红色或黑色)。通过对路径上节点颜色的约束,确保没有一条路径比其他路径长出一倍以上,从而实现近似平衡。
2.2 红黑树的性质
- 根节点和所有外部节点(NIL)的颜色是黑色。
- 从根到外部节点的途中没有连续两个红色节点。
- 所有从根到外部节点的路径上都有相同数目的黑色节点。
基于这些性质,可以推导出红黑树的高度 h 满足:h ≤ 2 log₂(n + 1)。这意味着增删查改的最坏时间复杂度稳定在 O(log n)。


