在计算机科学中,红黑树是一种自平衡的二叉搜索树。每个结点都增加了一个代表 “颜色”("红色 "或 “黑色”)的额外存储位,用于确保树在插入和删除时保持平衡。
通过对各个结点着色方式的限制,红黑树确保从根到空结点(NIL结点)的最长的可能路径长度不多于最短的可能路径的两倍。所以红黑树不是严格意义上的平衡二叉树(AVL),但对之进行平衡的代价较低, 其平均统计性能要强于 AVL 。
红黑树的性质:

为什么满足上面的性质,就能保证从根到叶子的最长路径长度不会超过最短路径的 2 倍?
是性质 3 和性质 4 确保了这个结果,一棵红黑树从根到 NIL 结点的所有路径上的黑色结点数是相等的,其中一条最短可能路径一定都是黑色,最长路径要保证尽可能长,在性质3的前提下只能在中间间隔式地插入红色结点,其最长的可能长度也不会超过最短可能长度的 2 倍。
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)
{}
};
template<class K, class V>
struct RBTree
{
typedef RBTreeNode<K, V> Node;
public:
private:
Node* _root;
};
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;
//...
return true;
}
我们的新结点先染为红色,因为插入红色对性质 3 可能有影响,对性质 4 没有影响,且性质 3 比性质 4 易于维护。
当通过搜索树规则找到要插入的位置时,该位置的父结点有可能是红,也可能是黑。如果是黑,即未破坏性质 3 ,不用调整。
如果父结点是红,则需要调整:
父结点是红,红结点一定不是根,所以一定还有祖父结点,且祖父结点一定是黑,所以这三个结点的情况是固定的。那么关键看叔叔结点。
下面 cur 为当前结点,p 为父结点,g 为祖父结点,u 为叔叔结点
情况一:cur 为红,p 为红,g 为黑,u 存在且为红
解决方案:p 和 u 设为黑,g 设为红。如果 g 是根结点,那么 g 直接设为黑,否则 g 设为 cur 继续向上调整。
因为要保持一条路径的黑色结点个数不变,所以最好把 p 改成红色,g 改成黑色,同时 u 要改成黑色,调整完看 g 是不是根结点,如果不是还要继续向上调,因为上面可能还有连续红色结点。
小三角表示子树,表示 cur 可能是新插入的结点(子树高度为 0),也可能是从下面调整上来的。

上图仅为p左u右的情况,其镜像同理:
//...
// 有父亲,且父亲为红才处理
while (parent && parent->_col == RED)
{
Node* grandfather = parent->_parent;
if (parent == grandfather->_left)
{
Node* uncle = grandfather->_right;
// 情况一
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续向上处理
cur = grandfather;
parent = cur->_parent;
}
else
{
//...
}
}
else
{
Node* uncle = grandfather->_left;
// 情况一
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续向上处理
cur = grandfather;
parent = cur->_parent;
}
else
{
//...
}
}
}
_root->_col = BLACK; // 不废话,根直接设为黑色
//...
情况二:cur 为红,p 为红,g 为黑,u 不存在/u 存在且为黑,cur 在 p 的外侧
如图,因为插入新结点前就应该满足红黑树,所以如果 u 不存在,那么 g 到 NIL 的每条路径的黑色结点个数一定是 2,所以插入前 g 的左路径只可能是 g-p-NIL,要获得连续红结点,cur 只能是新插入的结点。因为没有子树,所以没画小三角。
如果 u 存在且为黑,那么 g 到 NIL 的每条路径至少有 3 个黑色结点,如果 cur 是新插入的结点,那么插入前 g-p-NIL 路径只有 2 个黑色结点,与插入前满足红黑树的条件矛盾,所以 cur 一定是下面的子树新增结点之后通过情况一调整上来的。

解决方案:旋转 + 变色:以 g 为旋转点进行旋转,然后 p 变为黑色,g 变为红色。结束后 p 作为该子树的根结点,为黑色,不需要再向上调整。
此时仅仅通过变色无法满足要求了,所以要先旋转,旋转后,p 是新的根结点,但是 p 的左路径少了一个黑色,所以我们将 p 变黑,这样一来 p 的右路径又多了一个黑色,所以我们将 g 变红,最后保证了各路径的黑色结点数不变。
上图仅为 p 在 g 左边的情况,如果 p 在右边则需要右旋,注意旋转方向,旋转的处理和AVL树一样,具体参见博文:AVL树插入,旋转,详细图解与代码
//...父亲在左,叔叔在右
else // 情况二:叔叔不存在或叔叔存在且为黑
{
if (cur == parent->_left)
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
break;
}
//...叔叔在左,父亲在右
else // 情况二:叔叔不存在或叔叔存在且为黑
{
if (cur == parent->_right)
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
break;
}
//...
private:
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL) subRL->_parent = parent; // subRL有可能是空树,需要if判断
Node* ppNode = parent->_parent; // 提前记录祖先,后面用来连接新的parent
subR->_left = parent;
parent->_parent = subR;
if (parent == _root) // 如果parent就是根结点,那么新的根是subR
{
_root = subR;
_root->_parent = nullptr;
}
else // 否则需要祖先来连接新的parent(即subR),注意判断左右
{
if (parent == ppNode->_left)
{
ppNode->_left = subR;
}
else
{
ppNode->_right = subR;
}
subR->_parent = ppNode;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR) subLR->_parent = parent;
Node* ppNode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (parent == ppNode->_left)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
subL->_parent = ppNode;
}
}
情况三:cur 为红,p 为红,g 为黑,u 不存在/u 存在且为黑,cur 在 p 的内侧
解决方案:和情况二类似,不同点是要进行双旋+变色:先对 p 左/右旋,然后对 g 右/左旋,最后 cur 和 g 变色
第一次旋转对性质 4 没有造成影响,不用变色,第二次旋转和情况二的旋转一样,需要对 g 和 cur 这两个旋转点变色。

//...父亲在左,叔叔在右
else // 情况二:叔叔不存在或叔叔存在且为黑
{
if (cur == parent->_left) // 左左:右旋
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else // 左右:左右双旋
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
//...叔叔在左,父亲在右
else // 情况二:叔叔不存在或叔叔存在且为黑
{
if (cur == parent->_right) // 右右:左旋
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else // 右左:右左双旋
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
//...
关键看叔叔:
叔叔存在且为红,情况一,只需要变色。但仍需向上调整
叔叔不存在或存在且为黑,旋转+变色,旋转变色完成,符合红黑树性质,不再影响上层,处理结束。
public:
void LevelOrder()
{
queue<Node*> que;
if (_root != NULL) que.push(_root);
while (!que.empty())
{
int size = que.size();
for (int i = 0; i < size; i++)
{
Node* node = que.front();
que.pop();
cout << node->_kv.first << ' ';
if (node->_left) que.push(node->_left);
if (node->_right) que.push(node->_right);
}
cout << endl;
}
}
void Inorder()
{
_Inorder(_root);
cout << endl;
}
private:
void _Inorder(Node* root)
{
if (root == nullptr) return;
_Inorder(root->_left);
cout << root->_kv.first << ' ';
_Inorder(root->_right);
}
顺序插入0-99:
void testRBTree1()
{
const int N = 100;
RBTree<int, int> t{};
for (int i = 0; i < N; ++i)
{
t.Insert(make_pair(i, 0));
}
t.LevelOrder();
t.Inorder();
}

public:
void Height()
{
cout << "最长路径:" << _MaxHeight(_root) << endl;
cout << "最短路径:" << _MinHeight(_root) << endl;
}
private:
int _MaxHeight(Node* root)
{
if (root == nullptr) return 0;
int leftHeight = _MaxHeight(root->_left);
int rightHeight = _MaxHeight(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
int _MinHeight(Node* root)
{
if (root == nullptr) return 0;
int leftHeight = _MinHeight(root->_left);
int rightHeight = _MinHeight(root->_right);
return leftHeight < rightHeight ? leftHeight + 1 : rightHeight + 1;
}
插入随机值:
void testRBTree2()
{
const int N = 100;
vector<int> v;
v.reserve(N);
for (int i = 0; i < N; ++i)
{
v.push_back(rand());
}
RBTree<int, int> t{};
for (auto e : v)
{
t.Insert(make_pair(e, 0));
}
t.LevelOrder();
cout << endl;
t.Inorder();
cout << endl;
t.Height();
}

结果没问题。
但是这还不足以说明我们写的就是红黑树,下面写一个验证规则:
public:
bool IsRBTree()
{
if (_root == nullptr) return true; // 空树也是红黑树
if (_root->_col != BLACK) // 检查根结点
{
cout << "错误:根结点不为黑色" << endl;
return false;
}
// 获取任一条路径的黑色结点的个数,作为比较的基准值
size_t blackCount = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK) ++blackCount;
cur = cur->_left;
}
// 检查是否满足红黑树的性质,k用来记录路径中黑色结点的个数
size_t k = 0;
return _IsRBTree(_root, k, blackCount);
}
private:
bool _IsRBTree(Node* root, size_t k, const size_t blackCount)
{
// 走到空,判断黑色结点个数和基准值是否相等
if (root == nullptr)
{
if (k != blackCount)
{
cout << "错误:每条路径中黑色结点的个数不同" << endl;
return false;
}
return true;
}
if (root->_col == BLACK) ++k;
// 检查是否有连续的红色结点
Node* parent = root->_parent;
if (parent && parent->_col == RED && root->_col == RED)
{
cout << "错误:有连续的红结点" << endl;
return false;
}
return _IsRBTree(root->_left, k, blackCount) && _IsRBTree(root->_right, k, blackCount);
}
插入1000000个随机数:
void testRBTree3()
{
const int N = 1000000;
vector<int> v;
v.reserve(N);
for (int i = 0; i < N; ++i)
{
v.push_back(rand());
}
RBTree<int, int> t{};
for (auto e : v)
{
t.Insert(make_pair(e, 0));
}
//t.LevelOrder();
//cout << endl;
//t.Inorder();
//cout << endl;
t.Height();
cout << t.IsRBTree();
}

#pragma once
#include
#include
using namespace std;
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)
{}
};
template<class K, class V>
struct 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 (parent == grandfather->_left) // 父亲在左,叔叔在右
{
Node* uncle = grandfather->_right;
// 情况一
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续向上处理
cur = grandfather;
parent = cur->_parent;
}
else // 情况二:叔叔不存在或叔叔存在且为黑
{
if (cur == parent->_left) // 左左:右旋
{
RotateR(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else // 左右:左右双旋
{
RotateL(parent);
RotateR(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
else // 叔叔在左,父亲在右
{
Node* uncle = grandfather->_left;
// 情况一:叔叔存在且为红
if (uncle && uncle->_col == RED)
{
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
// 继续向上处理
cur = grandfather;
parent = cur->_parent;
}
else // 情况二:叔叔不存在或叔叔存在且为黑
{
if (cur == parent->_right) // 右右:左旋
{
RotateL(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
}
else // 右左:右左双旋
{
RotateR(parent);
RotateL(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
break;
}
}
}
_root->_col = BLACK; // 不废话,根直接设为黑色
return true;
}
void LevelOrder()
{
queue<Node*> que;
if (_root != NULL) que.push(_root);
while (!que.empty())
{
int size = que.size();
for (int i = 0; i < size; i++)
{
Node* node = que.front();
que.pop();
cout << node->_kv.first << ' ';
if (node->_left) que.push(node->_left);
if (node->_right) que.push(node->_right);
}
cout << endl;
}
}
void Inorder()
{
_Inorder(_root);
cout << endl;
}
void Height()
{
cout << "最长路径:" << _MaxHeight(_root) << endl;
cout << "最短路径:" << _MinHeight(_root) << endl;
}
bool IsRBTree()
{
if (_root == nullptr) return true; // 空树也是红黑树
if (_root->_col != BLACK) // 检查根结点
{
cout << "错误:根结点不为黑色" << endl;
return false;
}
// 获取任一条路径的黑色结点的个数,作为比较的基准值
size_t blackCount = 0;
Node* cur = _root;
while (cur)
{
if (cur->_col == BLACK) ++blackCount;
cur = cur->_left;
}
// 检查是否满足红黑树的性质,k用来记录路径中黑色结点的个数
size_t k = 0;
return _IsRBTree(_root, k, blackCount);
}
private:
bool _IsRBTree(Node* root, size_t k, const size_t blackCount)
{
// 走到空,判断黑色结点个数和基准值是否相等
if (root == nullptr)
{
if (k != blackCount)
{
cout << "错误:每条路径中黑色结点的个数不同" << endl;
return false;
}
return true;
}
if (root->_col == BLACK) ++k;
// 检查是否有连续的红色结点
Node* parent = root->_parent;
if (parent && parent->_col == RED && root->_col == RED)
{
cout << "错误:有连续的红结点" << endl;
return false;
}
return _IsRBTree(root->_left, k, blackCount) && _IsRBTree(root->_right, k, blackCount);
}
void _Inorder(Node* root)
{
if (root == nullptr) return;
_Inorder(root->_left);
cout << root->_kv.first << ' ';
_Inorder(root->_right);
}
int _MaxHeight(Node* root)
{
if (root == nullptr) return 0;
int leftHeight = _MaxHeight(root->_left);
int rightHeight = _MaxHeight(root->_right);
return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
}
int _MinHeight(Node* root)
{
if (root == nullptr) return 0;
int leftHeight = _MinHeight(root->_left);
int rightHeight = _MinHeight(root->_right);
return leftHeight < rightHeight ? leftHeight + 1 : rightHeight + 1;
}
void RotateL(Node* parent)
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL) subRL->_parent = parent; // subRL有可能是空树,需要if判断
Node* ppNode = parent->_parent; // 提前记录祖先,后面用来连接新的parent
subR->_left = parent;
parent->_parent = subR;
if (parent == _root) // 如果parent就是根结点,那么新的根是subR
{
_root = subR;
_root->_parent = nullptr;
}
else // 否则需要祖先来连接新的parent(即subR),注意判断左右
{
if (parent == ppNode->_left)
{
ppNode->_left = subR;
}
else
{
ppNode->_right = subR;
}
subR->_parent = ppNode;
}
}
void RotateR(Node* parent)
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR) subLR->_parent = parent;
Node* ppNode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
if (parent == _root)
{
_root = subL;
_root->_parent = nullptr;
}
else
{
if (parent == ppNode->_left)
{
ppNode->_left = subL;
}
else
{
ppNode->_right = subL;
}
subL->_parent = ppNode;
}
}
private:
Node* _root;
};