C++ 红黑树深度解析:原理、旋转与完整实现
红黑树本质上是一棵二叉搜索树,每个节点增加了一个颜色位(红色或黑色)。通过对路径上颜色的约束,它确保没有一条路径比其他路径长两倍,从而实现近似平衡。这意味着插入操作的时间复杂度在最坏情况下也是 O(logn)。
1. 红黑树的规则
红黑树必须满足以下四条规则:
- 每个节点要么是红色,要么是黑色。
- 根节点必须是黑色。
- 所有叶子节点(NIL)都是黑色的。
- 如果一个节点是红色,则它的两个子节点都必须是黑色(即不能有两个连续的红色节点)。
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
关于 NIL 节点:在《算法导论》等书籍中,NIL 被视为外部节点,代表空指针。它们都是黑色的,用于统一计算路径上的黑色节点数量。
为什么最长路径不超过最短路径的 2 倍?
- 最短路径:由规则 4 可知,每条路径的黑色节点数相同。极端情况下,最短路径全是黑色节点,长度为 bh(黑高)。
- 最长路径:由规则 2 和 3 可知,任意路径不能有连续红色节点。极端情况下,最长路径是一黑一红交替,长度为 2*bh。
- 结论:理论高度 h 满足 bh <= h <= 2*bh,因此时间复杂度稳定在 O(logn)。
效率对比
相比 AVL 树,红黑树对平衡的控制没那么严格。虽然两者查询效率同一档次,但在插入相同数量的节点时,红黑树的旋转次数更少。因此,在实际工程中(如 STL map/set),红黑树比 AVL 树更常用。
2. 红黑树的实现
2.1 结构定义
我们用枚举值表示颜色,并维护 parent 指针以便向上调整。
enum Color { BLACK, RED };
template<class K, class V>
struct RBTreeNode {
pair<K, V> _kv;
RBTreeNode* _left;
RBTreeNode* _right;
RBTreeNode* _parent;
Color _col;
RBTreeNode(const pair<K, V>& kv)
:_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED) {}
};
2.2 插入逻辑
插入新节点时,先按二叉搜索树规则找到位置。如果是空树,新节点设为黑色;否则新节点设为红色。如果父节点是黑色,无需调整;如果父节点是红色,则违反规则 3,需要修复。
修复过程主要涉及三种情况(设 c 为当前节点,p 为父节点,g 为祖父节点,u 为叔叔节点):
情况 1:变色
当叔叔节点 u 存在且为红色时,将 p 和 u 变黑,g 变红。然后将 g 视为新的 c 继续向上检查。此过程只变色不旋转。
情况 2:单旋 + 变色
当叔叔节点 u 不存在或为黑色时,单纯变色无法解决问题,需要旋转。
- 若 c 是 p 的左孩子,p 是 g 的左孩子(LL 型),以 g 为支点右旋,p 变黑,g 变红。
- 若 c 是 p 的右孩子,p 是 g 的右孩子(RR 型),以 g 为支点左旋,p 变黑,g 变红。


