一、红黑树的概念
红黑树是一种自平衡的二叉搜索树,每个节点增加一个存储位表示颜色(红色或黑色)。通过对任何一条从根到叶子的路径上各个结点的颜色进行约束,确保没有一条路径会比其他路径长出 2 倍,因而是接近平衡的。
1.1 红黑树的规则
- 每个结点不是红色就是黑色。
- 根结点是黑色的。
- 如果一个结点是红色的,则它的两个子结点必须是黑色的,即任意一条路径不会有连续的红色结点。
- 对于任意一个结点,从该结点到其所有 NULL 结点的简单路径上,均包含相同数量的黑色结点(最长路径 <= 2 * 最短路径)。
说明:《算法导论》等书籍补充了叶子结点 (NIL) 都是黑色的规则。这里指的叶子结点是我们说的空结点,有些书籍也称为外部结点。NIL 是为了方便准确标识出所有路径,实际实现中通常忽略 NIL 结点的具体操作,知道这个概念即可。
1.2 红黑树的效率
假设 N 是红黑树中结点数量,h 是最短路径的长度,那么 2^h - 1 <= N < 2^(2h) - 1,由此推出 h ≈ logN。这意味着红黑树增删查改最坏也就是走最长路径 2logN,时间复杂度依然是 O(logN)。
红黑树的表达相对 AVL 树要抽象一些。AVL 树通过高度差直观地控制平衡,红黑树通过 4 条规则的颜色约束间接实现了近似平衡。两者效率在同一档次,但相对而言,插入相同数量的结点,红黑树的旋转次数更少,因为它对平衡的控制没那么严格。
二、红黑树的实现
2.1 红黑树的结构
我们需要在节点中增加父指针以便回溯调整,同时记录颜色。
// 枚举值表示颜色
enum Colour { RED, BLACK };
template<class K, class V>
struct RBTreeNode {
pair<K, V> _kv;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
Colour _col;
RBTreeNode(const pair<K, V>& kv)
:_kv(kv), _left(nullptr), _right(nullptr), _parent(nullptr) {}
};
template<class K, class V>
class RBTree {
typedef RBTreeNode<K, V> Node;
public:
private:
Node* _root;
};
2.2 红黑树的插入
插入一个值按二叉搜索树规则进行插入,插入后观察是否符合红黑树的 4 条规则。
- 空树插入:新增结点是黑色结点。


