红黑树
1、红黑树的概念
红黑树是一棵二叉搜索树,他的每个节点增加一个存储位来表示节点的颜色,可以是红色或者黑色。通过对任何⼀条从根到叶子的路径上各个节点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出2倍,因而是接近平衡的。

2、红黑树的性质
- 根节点是黑色
- 如果有一个节点是红色的,则它的两个孩子必须是黑色的
- 不存在连续的两个红色节点
- 对于每个节点,从该节点到其所有后代节点的简单路径上,均包含相同数目的黑色节点
如何保证最长路径不会超过最短路径的两倍?
从根到NULL结点的每条路径都有相同数量的黑色结点,所以极端场景下,最短路径就就是全是黑色节点的路径,假设最短路径长度为bh
任意一条路径不会有连续的红色结点,所以极端场景下,最长的路径就是一
黑一红间隔组成,那么最长路径的长度为2*bh
因此,最长路径一定是一黑一红的排列,最短路径全为黑节点,任意一条路径上的黑色节点的占比一定是大于等于1/2的
3、红黑树节点的定义
cpp
// 枚举值表示颜色
enum Colour
{
RED,
BLACK
};
// 这里我们默认按key/value结构实现
template<class K, class V>
struct RBTreeNode
{
// 这里更新控制平衡也要加入parent指针
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)
{ }
};
新节点的默认颜色是RED,如果新插入的节点颜色是BLACK,那么就违反了红黑树的第四条性质,如果新插入的节点默认是RED,则不会出现这样的结果
4、红黑树的插入操作
红黑树的插入可以分为两步:
- 按二叉搜索树的规则进行插入
- 插入后观察是否符合红黑树的性质
因为新插入的节点默认颜色为RED,所以如果父节点是黑色的,没有违反任何规则,插入结束,但当双亲节点为RED时,就违反了性质三(不存在连续的两个红色节点),此时需要分情况讨论
把新增结点标识为c (cur),c的父亲标识为p(parent),p的父亲标识为
g(grandfather),p的兄弟标识为u(uncle)
4.1 情况1:c为红,p为红,g为黑,u存在且为红




解决方式:将parent和uncle变为黑,grandfather变红,再把grandfather当做新的cur,继续向上调整
4.2 情况2:c为红,p为红,g为黑,u不存在或者u存在且为黑
uncle不存在,则cur一定是新增节点,uncle存在且为黑,,则cur一定不是新增,cur之前是黑色的,是在cur的子树中插入,符合情况1,变色将cur从黑色变成红色,更新上来的
解决方式:p必须变黑,才能解决,连续红色结点的问题,u不存在或者是黑色的,这里单纯的变色无法解决问题,需要旋转+变色
4.2.1 单旋+变色



4.2.2 双旋+变色



4.3 插入完整代码
cpp
public:
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
// ========== 2. 二叉搜索树查找插入位置 ==========
Node* parent = nullptr; // 记录新节点最终要挂载的父节点
Node* cur = _root; // 遍历指针,从根开始向下搜索
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
// ========== 3. 创建新节点并挂载到父节点 ==========
// 循环结束cur为空,此处新建节点,cur指向新节点
cur = new Node(kv);
cur->_col = RED; //新增节点必须是红色
// 判断新节点是父节点的左孩子还是右孩子
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
// 双向绑定父子指针:新节点的父指向parent
cur->_parent = parent;
// ========== 4. 红黑树平衡修复循环 ==========
// 进入循环条件:父节点存在 且 父节点是红色
// 冲突根源:cur红 + parent红 → 连续红色节点,违反红黑树规则
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
// 分支一:父节点是祖父的左孩子
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 叔叔存在且为红->变色
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK; // 父变黑
uncle->_col = BLACK; //叔变黑
grandfather->_col = RED; // 祖父变红
// 父当做新的cur,回到循环头部继续向上修复
cur = grandfather;
parent = cur->_parent;
}
// 场景2/3:叔父不存在 或 叔父黑色,必须旋转修复
else // 叔叔不存在,或者叔叔存在且为黑->旋转+变色
{
// 场景2:cur是parent的左孩子,同侧结构 → 右单旋
if (cur == parent->_left)
{
// g
// p u
//c
// 右单旋
RotateR(grandfather);
parent->_col = BLACK; // 旋转后p成为局部子树根,染黑消除连续红
grandfather->_col = RED; // 原祖父下沉,染红
}
// 场景3:cur是parent的右孩子,异侧结构 → 左右双旋
else
{
// g
// p u
// c
// 左右双旋
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;// 旋转后cur成为局部根,染黑
grandfather->_col = RED;// 祖父下沉染红
}
break;
}
}
// 分支二:父节点是祖父的右孩子(和左侧逻辑完全对称)
else // grandfather->_right == parent
{
// g
// u p
Node* uncle = grandfather->_left;
// 叔叔存在且为红,变色即可
// 场景1:叔父存在且红色,仅变色
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上处理
cur = grandfather;
parent = cur->_parent;
}
// 场景2/3:叔父空/黑色,旋转修复
else // 叔叔不存在,或者存在且为黑
{
// 情况二:叔叔不存在或者存在且为黑
// 旋转+变色
// g
// u p
// c
// 场景2:cur是parent的右孩子,同侧结构 → 单左旋
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK; // p变局部根,染黑
grandfather->_col = RED; // g下沉染红
}
// 场景3:cur是parent的左孩子,异侧结构 → 右左双旋
else
{ // g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK; // cur变局部根,染黑
grandfather->_col = RED; // g下沉染红
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
private:
// 右单旋
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* parentParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
// 左单旋
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
5、红黑树的验证
- 枚举颜色类型,天然实现保证了颜色不是黑色就是红色
- 前序遍历检查
- 前序遍历,遍历过程中用形参记录跟到当前结点的blackNum(黑色结点数量),前序遍历遇到黑色结点就++blackNum,走到空就计算出了一条路径的黑色结点数量。再任意⼀条路径黑色结点数量作为参考值,依次比较即可
cpp
bool Check(Node* cur, int blackNum, const int blackNumRef)
{
if (cur == nullptr)
{
if (blackNum != blackNumRef)
{
cout << "黑色节点的数量不相等" << endl;
return false;
}
return true;
}
if (cur->_col == RED && cur->_parent && cur->_parent->_col == RED)
{
cout << cur->_kv.first << "->" << "连续的红色节点" << endl;
return false;
}
if (cur->_col == BLACK)
++blackNum;
// 用值传递
return Check(cur->_left, blackNum, blackNumRef)
&& Check(cur->_right, blackNum, blackNumRef);
}
6、红黑树的其他功能函数
_Height:获取整棵红黑树的高度
Find:根据 key 查找对应节点
_Size:递归统计以root为根的子树节点总数
IsBalance:整体校验红黑树是否合法,满足全部红黑规则
InOrder :对外调用中序遍历,打印树所有 key,内部调用私有递归_InOrder,遍历完成换行。二叉搜索树中序遍历结果是升序有序序列
,用来验证 BST 结构是否正确。
红黑树的总代码:
cpp
#pragma once
// 枚举值表示颜色
enum Colour
{
RED,
BLACK
};
// 这里我们默认按key/value结构实现
template<class K, class V>
struct RBTreeNode
{
// 这里更新控制平衡也要加入parent指针
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:
bool Insert(const pair<K, V>& kv)
{
if (_root == nullptr)
{
_root = new Node(kv);
_root->_col = BLACK;
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (cur->_kv.first > kv.first)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
cur = new Node(kv);
cur->_col = RED; //新增节点必须是红色
if (parent->_kv.first < kv.first)
{
parent->_right = cur;
}
else
{
parent->_left = cur;
}
cur->_parent = parent;
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (grandfather->_left == parent)
{
Node* uncle = grandfather->_right;
// 叔叔存在且为红->变色
if (uncle && uncle->_col == RED)
{
parent->_col = BLACK;
uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上处理
cur = grandfather;
parent = cur->_parent;
}
else // 叔叔不存在,或者叔叔存在且为黑->旋转+变色
{
if (cur == parent->_left)
{
// g
// p u
//c
// 右单旋
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{
// g
// p u
// c
// 左右单旋
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else // grandfather->_right == parent
{
// g
// u p
Node* uncle = grandfather->_left;
// 叔叔存在且为红,-》变色即可
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续往上处理
cur = grandfather;
parent = cur->_parent;
}
else // 叔叔不存在,或者存在且为黑
{
// 情况二:叔叔不存在或者存在且为黑
// 旋转+变色
// g
// u p
// c
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else
{ // g
// u p
// c
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK;
return true;
}
void InOrder()
{
_InOrder(_root);
cout << endl;
}
bool IsBalance()
{
if (_root == nullptr)
return true;
if (_root->_col == RED)
return false;
// 黑色节点数量参考值
Node* leftMost = _root;
int blackRef = 0;
while (leftMost)
{
if (leftMost->_col == BLACK)
++blackRef;
leftMost = leftMost->_left;
}
return Check(_root, 0, blackRef);
}
int Height()
{
return _Height(_root);
}
int Size()
{
return _Size(_root);
}
Node* Find(const K& key)
{
Node* cur = _root;
while (cur)
{
if (cur->_kv.first < key)
{
cur = cur->_right;
}
else if (cur->_kv.first > key)
{
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
private:
int _Size(Node* root)
{
return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
}
int _Height(Node* root)
{
if (root == nullptr)
return 0;
int leftHeight = _Height(root->_left);
int rightHeight = _Height(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
bool Check(Node* cur, int blackNum, const int blackNumRef)
{
if (cur == nullptr)
{
if (blackNum != blackNumRef)
{
cout << "黑色节点的数量不相等" << endl;
return false;
}
return true;
}
if (cur->_col == RED && cur->_parent && cur->_parent->_col == RED)
{
cout << cur->_kv.first << "->" << "连续的红色节点" << endl;
return false;
}
if (cur->_col == BLACK)
++blackNum;
return Check(cur->_left, blackNum, blackNumRef)
&& Check(cur->_right, blackNum, blackNumRef);
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_kv.first << " ";
//cout << root->_kv.first << ":" << root->_kv.second << endl;
_InOrder(root->_right);
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR)
subLR->_parent = parent;
Node* parentParent = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
subL->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subL;
}
else
{
parentParent->_right = subL;
}
subL->_parent = parentParent;
}
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL)
subRL->_parent = parent;
Node* parentParent = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
if (parent == _root)
{
_root = subR;
subR->_parent = nullptr;
}
else
{
if (parentParent->_left == parent)
{
parentParent->_left = subR;
}
else
{
parentParent->_right = subR;
}
subR->_parent = parentParent;
}
}
private:
Node* _root = nullptr;
};
7、红黑树与AVL树的比较
AVL树 对于平衡的要求会更高,AVL树查询节点的时间复杂度为 logN,红黑树的最长路径可以为最短路径的2倍,因此查询节点的时间复杂度为 log 2*N,红黑树实现比较简单,所以实际运用中红黑树更多,更广泛