C++ 红黑树:原理、旋转与完整实现
1. 初识红黑树
红黑树本质上是一棵二叉搜索树,其每个节点增加了一个颜色存储位(红色或黑色)。通过对从根到叶子路径上节点颜色的约束,红黑树确保没有一条路径比其他路径长出两倍,因此它是近似平衡的。这意味着红黑树的插入、删除和查找操作在最坏情况下的时间复杂度均为 O(logn)。
2. 红黑树的规则
2.1 四条核心规则
- 每个节点要么是红色,要么是黑色。
- 根节点必须是黑色。
- 所有叶子节点(NIL)都是黑色的。
- 如果一个节点是红色,则它的两个子节点必须是黑色(即不能有两个连续的红色节点)。
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
说明:这里的叶子节点指的是空节点(NIL),而非传统意义上的终端节点。引入 NIL 是为了方便统一处理路径计数问题。
2.2 为什么最长路径不超过最短路径的 2 倍?
根据规则 4,任意路径不会有连续红色节点,极端情况下最长路径由一黑一红交替组成;而根据规则 5,最短路径全为黑色节点。假设黑色高度为 bh,则最短路径长度为 bh,最长路径长度约为 2*bh。因此,红黑树的高度始终保持在 O(logn) 级别。
2.3 效率分析
相比 AVL 树,红黑树对平衡性的控制不那么严格,因此在插入相同数量节点时,红黑树的旋转次数更少。这使得红黑树在实际工程中应用更为广泛,如 STL 中的 map 和 set 底层均采用红黑树实现。
3. 红黑树的实现
3.1 结构定义
我们使用枚举值表示颜色,并定义节点结构体,包含键值对、左右子节点指针、父节点指针及颜色属性。
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) {}
};
3.2 插入逻辑与调整
插入新节点时,首先按二叉搜索树规则找到位置,默认将新节点设为红色。若违反规则(如父节点为红色),则需进行变色或旋转调整。主要分为三种情况:
-
情况 1:叔叔节点存在且为红色
- 操作:父节点和叔叔节点变黑,祖父节点变红。
- 结果:当前节点变为祖父节点,继续向上检查。
-
情况 2:叔叔节点不存在或为黑色,且当前节点与父节点同侧
- 操作:单旋 + 变色。
- 结果:父节点变黑,祖父节点变红,无需继续向上。
-
情况 3:叔叔节点不存在或为黑色,且当前节点与父节点异侧
- 操作:双旋 + 变色。


