C++ 伸展树与红黑树原理及实现详解
在平衡二叉搜索树(BBST)的家族中,伸展树(Splay Tree)和红黑树(Red-Black Tree)是两类极具代表性的结构。它们各有千秋:伸展树摒弃了严格的平衡执念,通过'伸展'操作将最近访问的节点移至根节点,利用局部性原理优化频繁访问场景;而红黑树则通过颜色约束确保最坏情况下的 O(log n) 性能,是 C++ STL 中 std::map、std::set 等容器的底层核心。
本文将深入剖析这两种数据结构的核心机制,并提供完整的 C++ 模拟实现代码。
1. 伸展树介绍
伸展树是一种自调整的二叉搜索树。与 AVL 树不同,它不强制维持全局平衡,而是倾向于让被频繁访问的节点靠近根部。每当执行搜索、插入或删除操作时,伸展树都会对目标节点执行一次'伸展'(Splaying)过程,将其移动到根节点位置。
1.1 旋转策略
伸展操作主要依赖三种旋转模式,目的是将目标节点 s 向根方向移动:
- 单旋转(Zig):当
s的父节点是根节点时,直接围绕父节点旋转。 - 一字形旋转(Zig-Zig):当
s与其父节点p、祖父节点g同侧(如左 -左或右 -右)时,先绕p旋转g,再绕s旋转p。 - 之字形旋转(Zig-Zag):当
s与其父节点p、祖父节点g异侧(如左 -右或右 -左)时,先绕s旋转p,再绕s旋转g。
这种策略使得频繁访问的节点始终处于较浅的位置,虽然单次操作可能达到 O(n),但在 m >= n 次操作的序列下,均摊时间复杂度为 O(m log n)。
2. 红黑树介绍
红黑树是一棵带有颜色属性的二叉搜索树,每个节点存储红色或黑色。通过对路径上颜色的约束,确保没有一条路径比其他路径长出一倍以上。
2.1 核心性质
- 每个节点要么是红色,要么是黑色。
- 根节点是黑色的。
- 所有叶子节点(NIL 节点)都是黑色的。
- 如果一个节点是红色的,则它的两个子节点都是黑色的(即不存在连续的两个红色节点)。
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点(黑高度)。
2.2 效率分析
由于上述性质,红黑树的高度 h 满足 h ≤ 2 log₂(n+1)。这意味着搜索、插入、删除的最坏时间复杂度均为 O(log n)。相比 AVL 树,红黑树在插入和删除时的旋转次数通常更少,因为它对平衡的要求相对宽松,更适合写多读少的场景。
3. 红黑树的实现
实现红黑树的关键在于处理插入后的修复逻辑。新插入的节点默认为红色,如果导致违反性质 4(连续红色),则需要通过变色或旋转来恢复平衡。
3.1 节点定义
我们采用哨兵节点(Sentinel Node)来简化边界处理,代替 NULL 指针。
#include <iostream>
#include <algorithm>
using namespace std;
enum Color { RED, BLACK };
< K, V>
{
K key;
V value;
Color color;
RBNode* parent;
RBNode* left;
RBNode* right;
( K& k, V& v, Color c = RED)
: (k), (v), (c), (), (), () {}
};


