引言
学完 AVL 树后,很多人会问:明明 AVL 树是'严格平衡'的二叉搜索树,查询效率理论上更高,为什么 C++ STL 的 map/set、Linux 内核里的关键结构,偏偏选红黑树而不用它?难道'更平衡'反而成了缺点?
其实答案藏在'工程取舍'里。红黑树的精髓,从来不是比 AVL 树更平衡,而是在'查询效率'和'写入开销'之间找最优解。它不像 AVL 树那样追求极致的矮,而是用'近似平衡'(最长路径不超过最短路径 2 倍)的规则,换来了插入删除时更少的旋转、更低的空间开销,刚好适配了工程里读写频繁、追求均衡性能的大多数场景。
这篇内容不绕弯子,直接从红黑树的 5 条核心性质讲起,拆透'新节点为啥默认红色''插入时 3 种情况怎么处理'这些经典难点,再对比 AVL 树说清'什么时候该选谁',最后附上可运行的手撕代码和验证逻辑。
红黑树的概念
红黑树本质上是一种二叉搜索树,但在每个结点上增加了一个存储位表示结点的颜色,可以是 Red(红)或 Black(黑)。
红黑树的性质
为了保证平衡性,红黑树必须满足以下 5 条规则:
- 每个结点不是红色就是黑色。
- 根节点是黑色的。
- 任何路径没有连续的红节点(即红节点的子节点必须是黑色)。
- 各路径黑节点的个数相同(即从任一节点到其所有叶子节点的路径上,黑色节点数量相等)。
- 每个叶子结点都是黑色的(此处的叶子结点指的是那个空结点,通常称为 NIL 节点)。
关于路径:从根节点到空节点算一条路径。路径长度计算时,空节点不算,根节点要算。
这些性质保证了红黑树的最长路径长度不超过最短路径长度的 2 倍。因为一红后面必会跟一黑,且各路径黑节点的个数是一样的。
红黑树增删查改的时间复杂度也是 O(logN),其中 N 是节点个数。空树也可以是红黑树。
AVL 树跟红黑树的比较
AVL 树和红黑树在查找时的性能是同一量级的,但两者的侧重点不同:
- AVL 树:平衡控制非常严格,插入和删除时的旋转次数很多。适合大量查找、少量修改的场景。
- 红黑树:平衡控制相对宽松,旋转次数少。虽然查找性能略逊于 AVL,但在各方面更加均衡,更适合频繁修改的场景。
红黑树的模拟实现
下面我们用 C++ 来实现一个红黑树。为了清晰起见,我们将结构体定义和核心逻辑分开展示。
1. 节点结构与枚举
首先定义颜色枚举和节点结构。注意,我们在构造函数中将新节点默认初始化为红色。
enum Colour { RED, BLACK };
template<class K, class V>
struct RBTreeNode {
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
pair<K, V> _kv;
Colour _col;
RBTreeNode(const pair<K, V>& kv)
: _left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED) {}
};
2. 插入逻辑详解
插入新节点的时候,默认先设置成红色的。如果设置为黑色,会违反第 4 条性质(黑高一致),调整难度更大;而设置为红色只可能违反第 3 条性质(连续红节点),更容易通过变色或旋转修复。


