红黑树详解
基本概念
红黑树是一种自平衡的二叉搜索树。它在每个节点上增加了一个存储位来表示颜色(红色或黑色),通过对路径上颜色的约束,确保从根到叶子的最长路径不会超过最短路径的两倍,从而近似保持平衡。
核心规则
要成为一棵合法的红黑树,必须满足以下五条性质:
- 节点颜色:每个节点要么是红色,要么是黑色。
- 根节点:根节点必须是黑色。
- 红色限制:如果一个节点是红色,则它的两个子节点必须是黑色。这意味着任意一条路径上不能出现连续的红色节点。
- 黑色高度:对于任意一个节点,从该节点到其所有叶子节点(NIL 节点)的简单路径上,均包含相同数量的黑色节点。
- 叶子节点:所有的叶子节点(NIL)都是黑色的。
注意:数路径时要数到空结点(NIL)。这保证了树的深度控制在 O(logN) 级别。
插入调整策略
插入新节点时,我们默认将其染为红色,然后按照二叉搜索树的规则找到位置。如果破坏了红黑树的性质,就需要通过变色和旋转来修复。主要涉及三种情况:
情况一:叔叔节点存在且为红色
当父节点和当前节点都是红色,而叔叔节点也是红色时,说明祖父节点必然是黑色。此时只需将父节点和叔叔节点变黑,祖父节点变红,然后将祖父节点视为新的当前节点继续向上检查。这种情况只变色不旋转。
情况二:叔叔节点不存在或为黑色(单旋 + 变色)
当父节点和当前节点同侧(例如都是左孩子),且叔叔节点不存在或为黑色时,需要对祖父节点进行右旋,并交换颜色。这通常发生在新增节点导致不平衡时。
情况三:叔叔节点不存在或为黑色(双旋 + 变色)
当父节点和当前节点异侧(例如父是左,当前是右)时,需要先对父节点进行左旋,转化为情况二,然后再对祖父节点进行右旋。双旋操作能有效处理这种'之'字形的结构。
C++ 代码实现
下面是一个基于模板的红黑树实现示例。为了便于理解,我将核心逻辑整理成了清晰的类结构。在实际工程中,通常会配合 KeyOfT 函数对象来提取比较键值。
#include <iostream>
#include <vector>
using namespace std;
enum Colour { RED, BLACK };
template<class T>
struct RBTreeNode {
T _data;
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
Colour _col;
RBTreeNode(const T& data)
:_data(data), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED) {}
};
template< , , >
{
RBTreeNode<T> Node;
:
{
(_root == ) {
_root = (data);
_root->_col = BLACK;
;
}
KeyOfT kot;
Node* parent = ;
Node* cur = _root;
(cur) {
((cur->_data) < (data)) {
parent = cur;
cur = cur->_right;
} ((cur->_data) > (data)) {
parent = cur;
cur = cur->_left;
} {
;
}
}
cur = (data);
cur->_col = RED;
((parent->_data) < (data)) {
parent->_right = cur;
} {
parent->_left = cur;
}
cur->_parent = parent;
(parent && parent->_col == RED) {
Node* grandfather = parent->_parent;
(parent == grandfather->_left) {
Node* uncle = grandfather->_right;
(uncle && uncle->_col == RED) {
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
} {
(cur == parent->_left) {
(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
} {
(parent);
(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
;
}
} {
Node* uncle = grandfather->_left;
(uncle && uncle->_col == RED) {
parent->_col = uncle->_col = BLACK;
grandfather->_col = RED;
cur = grandfather;
parent = cur->_parent;
} {
(cur == parent->_right) {
(grandfather);
parent->_col = BLACK;
grandfather->_col = RED;
} {
(parent);
(grandfather);
cur->_col = BLACK;
grandfather->_col = RED;
}
;
}
}
}
_root->_col = BLACK;
;
}
{
_InOrder(_root);
cout << endl;
}
:
_Size(Node* root) {
root == ? : _Size(root->_left) + _Size(root->_right) + ;
}
_Height(Node* root) {
(root == ) ;
leftHeight = _Height(root->_left);
rightHeight = _Height(root->_right);
leftHeight > rightHeight ? leftHeight + : rightHeight + ;
}
_InOrder(Node* root) {
(root == ) ;
_InOrder(root->_left);
cout << root->_data << ;
_InOrder(root->_right);
}
{
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
(subLR) subLR->_parent = parent;
Node* ppnode = parent->_parent;
subL->_right = parent;
parent->_parent = subL;
(parent == _root) {
_root = subL;
subL->_parent = ;
} {
(ppnode->_left == parent) ppnode->_left = subL;
ppnode->_right = subL;
subL->_parent = ppnode;
}
}
{
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
(subRL) subRL->_parent = parent;
Node* ppnode = parent->_parent;
subR->_left = parent;
parent->_parent = subR;
(parent == _root) {
_root = subR;
subR->_parent = ;
} {
(ppnode->_left == parent) ppnode->_left = subR;
ppnode->_right = subR;
subR->_parent = ppnode;
}
}
:
Node* _root = ;
};


