
👀樊梓慕:个人主页
🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》《Linux》《算法》
🌝每一个不曾起舞的日子,都是对生命的辜负
目录
如果没有现在的红黑树的话,那么可能set与map底层的数据结构就是AVL树了,那么红黑树的设计为什么能够取代AVL树的地位呢,红黑树的设计又有哪些奥秘,今天让我们一同来探索一下吧!
欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。
=========================================================================
GITEE相关代码:🌟樊飞 (fanfei_c) - Gitee.com🌟
=========================================================================
『 红黑树』,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或
Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的『 限制』,红黑树确保没有一条路径会比其他路径长出俩倍,因而是『 接近平衡』的。
红黑树不像AVL树那样绝对的平衡,而是接近平衡,
但是这个影响是常数级别的,接着往下看。

了解了红黑树的性质,你就明白了为什么红黑树是如何控制平衡的。
接下来我们构建极端场景来分析下为什么最长路径中节点的个数不会超过最短路径节点个数的两倍?
举例:

而且红黑树不是每次搜索都是2*logN。
AVL树与红黑树搜索效率基本平齐,但是AVL树实现更小的高度的方法是频繁地旋转,而旋转的开销还是比较大的,所以AVL树有点『 得不偿失』。
红黑树虽然也需要旋转来控制高度,但是引入了颜色控制,可以减少旋转的次数。
- enum Colour
- {
- RED,
- BLACK
- };
-
- template<class K, class V>
- struct RBTreeNode
- {
- RBTreeNode
* _left; - RBTreeNode
* _right; - RBTreeNode
* _parent; - pair
_kv; - Colour _col;
-
- RBTreeNode(const pair
& kv) - : _left(nullptr)
- , _right(nullptr)
- , _parent(nullptr)
- , _kv(kv)
- , _col(RED)
- {}
- };
思考:节点的默认颜色为什么是红色?
因为如果插入黑色节点就一定会破坏规则(每条路径上的黑色节点数相同),并且会影响其他路径。
所以我们需要设置节点的默认颜色为红色。
因为新节点的默认颜色是红色,因此
所以以下都是当新插入节点的父节点为红色时的情况:

g的颜色是确定为黑的,因为如果g不为黑,那么就证明上一层就已经不满足红黑树的规则了。
此时c一定是新插入的节点,并且已经破坏了没有连续的红色节点的规则,所以我们需要进行调整。
调整的办法:p/u变黑,g变红。

g变红的目的是为了保持当前分支黑色节点的数目不变。
但这里注意:

同样的g的颜色是确定为黑的,因为如果g不为黑,那么就证明上一层就已经不满足红黑树的规则了。
此时单纯地变色已经无法处理了,所以此时我们需要进行旋转。
很明显根据之前学习的AVL树,面对如上图情况时我们需要对g进行右旋。
即当p为g的左,cur为p的左时对g进行右旋:

注意:之前分析由于cur是因为之前的调整才变红的,所以cur向下的路径中一定有一个黑色节点与叔叔抵消,此时达到平衡。
如果叔叔不存在也是一样的对g进行右旋即可:

那么假如cur在p的右子树上呢?此时则需要先左旋再右旋。
即当p为g左,cur为p的右时进行左右双旋,然后变色。

同样的如果叔叔不存在也是一样的进行左右双旋,然后变色。

总结下来就以上这些种情况,相信你掌握了左面的这些种情况,右面也非常简单。
- bool Insert(const pair
& 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); // 红色的
- 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 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;
- subR->_parent = nullptr;
- }
- else
- {
- if (ppnode->_left == parent)
- {
- 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;
-
- subL->_right = parent;
-
- Node* ppnode = parent->_parent;
- parent->_parent = subL;
-
- if (parent == _root)
- {
- _root = subL;
- subL->_parent = nullptr;
- }
- else
- {
- if (ppnode->_left == parent)
- {
- ppnode->_left = subL;
- }
- else
- {
- ppnode->_right = subL;
- }
- subL->_parent = ppnode;
- }
- }
红黑树的验证分为两步:
首先要对树是否满足搜索特性进行检测,这一部分我们只需要对树进行中序遍历,观察得到的序列是否为有序序列即可。
其次就是如何判断该树满足红黑树的特性呢?
其实就是看是否满足红黑树的规则即可。
即:
首先根节点是否为黑很好检测。
其次是否有连续的红色节点,由于我们采用的是三叉链式结构,我们维护了父亲节点,所以只需要判断当前节点和父亲节点是否同时为红即可。
最后就是如何判断每条路径的黑色节点数目是否相同,其实最简单的做法就是先统计一下最左路径的黑色节点数目,然后将该值作为参考,检验其他路径是否与该值相同即可。
- bool Check(Node* cur, int blackNum, int refBlackNum)
- {
- if (cur == nullptr)
- {
- if (refBlackNum != blackNum)
- {
- cout << "黑色节点的数量不相等" << endl;
- return false;
- }
- return true;
- }
-
- if (cur->_col == RED && cur->_parent->_col == RED)
- {
- cout << cur->_kv.first << "存在连续的红色节点" << endl;
- return false;
- }
-
- if (cur->_col == BLACK)
- ++blackNum;
-
- return Check(cur->_left, blackNum, refBlackNum)
- && Check(cur->_right, blackNum, refBlackNum);
- }
-
- bool IsBalance()
- {
- if (_root && _root->_col == RED)
- return false;
-
- int refBlackNum = 0;
- Node* cur = _root;
- while (cur)
- {
- if (cur->_col == BLACK)
- refBlackNum++;
-
- cur = cur->_left;
- }
- return Check(_root, 0, refBlackNum);
- }
- enum Colour
- {
- RED,
- BLACK
- };
-
- template<class K, class V>
- struct RBTreeNode
- {
- RBTreeNode
* _left; - RBTreeNode
* _right; - RBTreeNode
* _parent; - pair
_kv; - Colour _col;
-
- RBTreeNode(const pair
& kv) - :_left(nullptr)
- , _right(nullptr)
- , _parent(nullptr)
- , _kv(kv)
- , _col(RED)
- {}
- };
-
- template<class K, class V>
- class RBTree
- {
- typedef RBTreeNode
Node; - public:
- bool Insert(const pair
& 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); // 红色的
- 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 RotateL(Node* parent)
- {
- ++rotateSize;
-
- 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;
- subR->_parent = nullptr;
- }
- else
- {
- if (ppnode->_left == parent)
- {
- ppnode->_left = subR;
- }
- else
- {
- ppnode->_right = subR;
- }
- subR->_parent = ppnode;
- }
- }
-
- void RotateR(Node* parent)
- {
- ++rotateSize;
-
- 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;
- subL->_parent = nullptr;
- }
- else
- {
- if (ppnode->_left == parent)
- {
- ppnode->_left = subL;
- }
- else
- {
- ppnode->_right = subL;
- }
- subL->_parent = ppnode;
- }
- }
-
- void _InOrder(Node* root)
- {
- if (root == nullptr)
- return;
-
- _InOrder(root->_left);
- cout << root->_kv.first << endl;
- _InOrder(root->_right);
- }
-
- void InOrder()
- {
- _InOrder(_root);
- }
-
- bool Check(Node* cur, int blackNum, int refBlackNum)
- {
- if (cur == nullptr)
- {
- if (refBlackNum != blackNum)
- {
- cout << "黑色节点的数量不相等" << endl;
- return false;
- }
-
- //cout << blackNum << endl;
- return true;
- }
-
- if (cur->_col == RED && cur->_parent->_col == RED)
- {
- cout << cur->_kv.first << "存在连续的红色节点" << endl;
- return false;
- }
-
- if (cur->_col == BLACK)
- ++blackNum;
-
- return Check(cur->_left, blackNum, refBlackNum)
- && Check(cur->_right, blackNum, refBlackNum);
- }
-
- bool IsBalance()
- {
- if (_root && _root->_col == RED)
- return false;
-
- int refBlackNum = 0;
- Node* cur = _root;
- while (cur)
- {
- if (cur->_col == BLACK)
- refBlackNum++;
-
- cur = cur->_left;
- }
-
- return Check(_root, 0, refBlackNum);
- }
-
- size_t Size()
- {
- return _Size(_root);
- }
-
- size_t _Size(Node* root)
- {
- if (root == NULL)
- return 0;
-
- return _Size(root->_left)
- + _Size(root->_right) + 1;
- }
-
- 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 NULL;
- }
-
- 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;
- }
-
- int Height()
- {
- return _Height(_root);
- }
-
- int GetRotateSize()
- {
- return rotateSize;
- }
-
- private:
- Node* _root = nullptr;
- int rotateSize = 0;
- };
注:rotateSize成员是用来记录旋转次数的,若不记录可以删除。
如上图,这是在一千万数据下得到的测试结果。
结论:红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是logN,红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,红黑树降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多(比如STL库中的set和map容器,linux内核等等)。而AVL树虽然在高度和搜索上优于红黑树,但基本可以忽略。
=========================================================================
如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容
🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎
🌟~ 点赞收藏+关注 ~🌟
=========================================================================