目录
红黑树(red-black tree)是这样的一棵二叉搜索树:树中的每个节点的颜色不是红色就是黑色。可以把一棵红黑树视为一棵扩充二叉树,用外部节点表示空指针。其特性描述如下:
根节点和所有外部节点的颜色是黑色。
从根节点到外部节点的途中没有连续两个节点的颜色是红色。
特性 2 意味着如果一个节点的颜色是红色,则它的左、右孩子的颜色必然是黑色。
对任一节点,所有从该节点到外部节点的路径上都有相同数目的黑色节点。
从红黑树中任一节点 x 出发(不包括节点 x),到达任一个外部节点的路径上的黑节点个数叫做节点 x 的黑高度,亦称为节点的阶,记作 bh(x)。红黑树的黑高度定义为其根节点的黑高度。
下图所示的二叉搜索树就是一棵红黑树,节点上边的数字为该节点的黑高度。

结论一:对任一节点 x,假设它的黑高度为 r,那么从节点 x 到任一外部节点的路径长度为 r ~ 2r。
注意:从节点 x 到任一外部节点的路径长度为该路径上的指针的个数。
结论二:假设 h 是一棵红黑树的高度(不包括外部节点),n 是红黑树中内部节点的个数, r 是红黑树的黑高度,则以下关系式成立:
(1)
;
(2)
;
(3)
。
证明:
(1) 由结论一即可证明。上图所示的红黑树的高度(不计外部节点)为 2r = 4。
(2) 因为红黑树的黑高度为 r,即从红黑树的第 1 层到第 r 层没有外部节点,所以内部节点的总数至少为
。在上图所示的红黑树中,红黑树的黑高度为 r = 2,第 1 层和第 2 层总共有
个内部节点,而在第 3 层和第 4 层还有 7 个节点,则有
。
(3) 由 (2) 可得
,结合 (1),有
。
由此也可以知道红黑树的搜索、插入、删除操作的时间复杂度为 O(
)。
- enum Color
- {
- RED,
- BLACK
- };
-
- template<class K, class V>
- struct RBTNode
- {
- RBTNode
* _left; - RBTNode
* _right; - RBTNode
* _parent; - std::pair
_kv; - Color _clr;
-
- RBTNode(const std::pair
& kv = std::pair(), Color clr = RED) - : _left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _clr(clr)
- { }
- };
如果插入前树非空,若新节点被染成黑色,将违反红黑树的特性 3,即所有从根节点到外部节点的路径上的黑色节点个数变得不相等了,因此如果插入前树非空,新插入的节点应该被然成红色。
如果新插入的节点的父节点是黑色节点,则没有违反红黑树的任何特性,不需要做调整,即插入结束;如果新插入的节点的父节点是红色节点,则违反了红黑树的特性 2,即出现了连续两个红色节点,需要做平衡调整。
设 *cur 为当前节点,它和它的父结点 *parent 都是红色节点,它的祖父节点 *grandparent 是黑色节点,即出现了连续两个红色节点的情形,此时通过考查其叔叔节点 *uncle 做相应的平衡调整。
如果 *uncle 是红色节点,则将 *parent 和 *uncle 改为黑色节点,将 *grandparent 改为红色节点。例如:

如果
*grandparent是根节点,则将其再改为黑色节点,调整完成。如果
*grandparent是子树的根,则其父节点也可能是红色节点,所以还需要继续往上调整。
如果 *uncle 不存在或者是黑色节点,此时又有以下 4 种情况:
(1) *parent 是 *grandparent 的左孩子,*cur 是 *parent 的左孩子。在这种情况下,只要做一次右单旋,再将 *parent 改为黑色节点,*grandparent 改为红色节点,就可恢复红黑树的特性,并结束平衡调整。

(2) *parent 是 *grandparent 的左孩子,*cur 是 *parent 的右孩子。在这种情况下,只要做一次先左后右的双旋转,再将 *cur 改为黑色节点,*grandparent 改为红色节点,就可恢复红黑树的特性,并结束平衡调整。

(3) *parent 是 *grandparent 的右孩子,*cur 是 *parent 的右孩子。
(4) *parent 是 *grandparent 的右孩子,*cur 是 *parent 的左孩子。
情况 (2)、(3) 和情况 (1)、(2) 是镜像的。
- #pragma once
-
- #include
-
- enum Color
- {
- RED,
- BLACK
- };
-
- template<class K, class V>
- struct RBTNode
- {
- RBTNode
* _left; - RBTNode
* _right; - RBTNode
* _parent; - std::pair
_kv; - Color _clr;
-
- RBTNode(const std::pair
& kv = std::pair(), Color clr = RED) - : _left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _clr(clr)
- { }
- };
-
- template<class K, class V>
- class RBT
- {
- typedef RBTNode
Node; - public:
- RBT() : _root(nullptr) { }
-
- ~RBT()
- {
- Destroy(_root);
- }
-
- bool isValidRBT()
- {
- if (_root == nullptr)
- return true;
-
- if (_root->_clr == RED)
- return false;
-
- // 获取从根节点到任一外部节点的路径上的黑色节点的个数
- size_t blackCount = 0;
- Node* cur = _root;
- while (cur)
- {
- if (cur->_clr == BLACK)
- ++blackCount;
- cur = cur->_left;
- }
-
- size_t k = 0; // k 用来路径种的黑色节点个数
- return _isValidRBT(_root, blackCount, k);
- }
-
- bool insert(const std::pair
& kv) - {
- if (_root == nullptr)
- {
- _root = new Node(kv, BLACK);
- return true;
- }
-
- Node* parent = nullptr;
- Node* cur = _root;
- while (cur)
- {
- parent = cur;
- if (kv.first < cur->_kv.first)
- cur = cur->_left;
- else if (kv.first > cur->_kv.first)
- cur = cur->_right;
- else
- return false;
- }
-
- // 如果插入前树非空,新插入的节点应该是红色节点
- cur = new Node(kv, RED);
- cur->_parent = parent;
-
- // 出现连续两个红色节点的情形
- while (parent && parent->_clr == RED)
- {
- Node* grandparent = parent->_parent;
- Node* uncle;
- if (grandparent->_left == parent)
- uncle = grandparent->_right;
- else
- uncle = grandparent->_left;
-
- // 如果 *uncle 是红色节点
- if (uncle && uncle->_clr == RED)
- {
- parent->_clr = uncle->_clr = BLACK;
- grandparent->_clr = RED;
-
- cur = grandparent;
- parent = cur->_parent;
- }
- else // 如果 *uncle 不存在或者是黑色节点
- {
- if (grandparent->_left == parent && parent->_left == cur)
- {
- LL(grandparent);
- parent->_clr = BLACK;
- grandparent->_clr = RED;
- }
- else if (grandparent->_right == parent && parent->_right == cur)
- {
- RR(grandparent);
- parent->_clr = BLACK;
- grandparent->_clr = RED;
- }
- else if (grandparent->_left == parent && parent->_right == cur)
- {
- LR(grandparent);
- cur->_clr = BLACK;
- grandparent->_clr = RED;
- }
- else if (grandparent->_right == parent && parent->_left == cur)
- {
- RL(grandparent);
- cur->_clr = BLACK;
- grandparent->_clr = RED;
- }
- break;
- }
- }
- _root->_clr = BLACK;
- return true;
- }
- private:
- void Destroy(Node*& root)
- {
- // 【注意:root 为 _root 或者某个节点的左或右指针的引用】
- if (root == nullptr)
- return;
-
- Destroy(root->_left);
- Destroy(root->_right);
- delete root;
- root = nullptr;
- }
-
- bool _isValidRBT(Node* root, size_t blackCount, size_t k)
- {
- if (root == nullptr)
- {
- if (blackCount == k)
- return true;
- else
- return false; // 违反了特性 3
- }
-
- if (root->_clr == BLACK)
- {
- ++k;
- }
- else
- {
- if (root->_parent && root->_parent->_clr == RED)
- return false; // 违反了特性 2
- }
-
- return _isValidRBT(root->_left, blackCount, k)
- && _isValidRBT(root->_right, blackCount, k);
- }
-
- void LL(Node* parent)
- {
- Node* cur = parent->_left;
- Node* curRight = cur->_right;
-
- parent->_left = curRight;
- if (curRight)
- curRight->_parent = parent;
-
- cur->_right = parent;
- Node* tmp = parent->_parent;
- parent->_parent = cur;
- cur->_parent = tmp;
-
- if (tmp == nullptr)
- {
- _root = cur;
- }
- else
- {
- if (tmp->_left == parent)
- tmp->_left = cur;
- else
- tmp->_right = cur;
- }
- }
-
- void RR(Node* parent)
- {
- Node* cur = parent->_right;
- Node* curLeft = cur->_left;
-
- parent->_right = curLeft;
- if (curLeft)
- curLeft->_parent = parent;
-
- cur->_left = parent;
- Node* tmp = parent->_parent;
- parent->_parent = cur;
- cur->_parent = tmp;
-
- if (tmp == nullptr)
- {
- _root = cur;
- }
- else
- {
- if (tmp->_left == parent)
- tmp->_left = cur;
- else
- tmp->_right = cur;
- }
- }
-
- void LR(Node* parent)
- {
- Node* cur = parent->_left;
- RR(cur);
- LL(parent);
- }
-
- void RL(Node* parent)
- {
- Node* cur = parent->_right;
- LL(cur);
- RR(parent);
- }
- private:
- Node* _root;
- };
- #include "RBT.h"
- #include
- #include
- #include
- using namespace std;
-
- void TestRBT()
- {
- srand((unsigned int)time(0));
- size_t N = 10000;
- vector<int> v;
- for (size_t i = 0; i < N; ++i)
- {
- v.push_back(rand());
- }
-
- RBT<int, int> t;
- for (const auto& e : v)
- {
- t.insert(make_pair(e, e));
- if (!t.isValidRBT())
- cout << "插入操作有误!" << endl;
- }
- cout << "插入完成~" << endl;
- }
-
- int main()
- {
- TestRBT();
- return 0;
- }
红黑树和 AVL 树的增删查改的效率都很高,时间复杂度为 O(),但红黑树不追求极致的平衡,其只需保证最长路径不超过最短路径的 2 倍,相对而言,降低了平衡旋转的次数,所以在经常进行增删的结构中,红黑树的性能比 AVL 树更好,并且红黑树的实现更简单,所以在实际应用中,使用红黑树的也更多。