目录
一、引言
红黑树(Red-Black Tree)是一种自平衡的二叉搜索树,它可以在插入、删除和查找操作中保持相对高效的性能。由于其独特的性质,红黑树在计算机科学中得到了广泛的应用,特别是在需要动态维护有序数据集合的场景中。本文将详细介绍红黑树的基本概念、性质、实现以及应用。
二、红黑树的基本概念
红黑树是一种特殊的二叉搜索树,它在每个节点上附加了一个颜色属性(红色或黑色),并通过以下五个性质来维持树的平衡:
- 每个节点要么是红色,要么是黑色。
- 根节点是黑色。
- 每个叶子节点(NIL节点,空节点)是黑色。
- 如果一个节点是红色的,则它的两个子节点都是黑色的。
- 对于每个节点,从该节点到其所有后代叶子节点的简单路径上,均包含相同数目的黑色节点。
三、红黑树的性质
红黑树的性质保证了它在插入、删除和查找操作中的高效性。以下是一些关键性质:
- 高度平衡:由于性质5的保证,红黑树的高度大致为O(log n),其中n为树中节点的数量。因此,在红黑树中查找、插入和删除操作的时间复杂度均为O(log n)。
- 动态维护:红黑树在插入和删除节点时,通过一系列旋转和颜色调整操作来保持树的平衡。这些操作保证了红黑树在动态变化时仍能保持较高的性能。
四、红黑树的实现
红黑树的实现涉及到节点定义、插入操作、删除操作以及旋转和颜色调整等辅助操作。以下是一个简化的C++红黑树实现框架:
-
结构
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>
class RBTree
{
typedef RBTreeNode<K, V> Node;
public:
bool Insert(const pair<K, V>& kv);
void RotateR(Node* parent);
void RotateL(Node* parent);
private:
Node* _root = nullptr;
};
-
插入
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) { // 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 { 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 RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; parent->_left = subLR; if (subLR) subLR->_parent = parent; subL->_right = parent; Node* ppNode = parent->_parent; parent->_parent = subL; if (parent == _root) { _root = subL; _root->_parent = nullptr; } else { if (ppNode->_left == parent) { ppNode->_left = subL; } else { ppNode->_right = subL; } subL->_parent = ppNode; } } void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; parent->_right = subRL; if (subRL) subRL->_parent = parent; subR->_left = parent; Node* ppNode = parent->_parent; parent->_parent = subR; if (parent == _root) { _root = subR; _root->_parent = nullptr; } else { if (ppNode->_right == parent) { ppNode->_right = subR; } else { ppNode->_left = subR; } subR->_parent = ppNode; } }
五、红黑树的应用
红黑树在许多领域都有广泛的应用,包括但不限于:
- 关联容器:C++标准库中的
std::map
和std::set
就是基于红黑树实现的关联容器。它们支持高效的插入、删除和查找操作,并且能够动态地维护有序数据集合。 - 路由表:在计算机网络中,路由表通常使用红黑树来存储路由信息。由于路由表需要频繁地插入、删除和查找路由条目,红黑树的高效性使得路由表能够快速地响应网络变化。
- 数据库索引:在数据库中,索引是提高查询性能的关键。红黑树作为一种高效的动态数据结构,可以用于实现各种索引结构,如B+树等。