目录
1. 红黑树的概念
2. 红黑树的性质
3. 红黑树节点的定义
4. 红黑树性质分析
5. 红黑树插入节点简要分析(新插入节点的parent为红色)
5.1 简要分析1-变色:
5.2 简要分析2-变色+旋转
5.3 简要分析总结
6. 红黑树插入节点详细分析
6.1 情况一: cur为红,p为红,g为黑,u存在且为红
6.2 情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑
6.2.1 a/b/c/d/e是空树,uncle不存在,cur就是新增
6.2.2 a/b/c/d/e不是空树,uncle存在且为黑,cur原黑
6.3 红黑树插入代码实现
6.4 红黑树的验证
7. 红黑树的其他
7.1 红黑树的删除
7.2 红黑树与AVL树的比较
7.3 红黑树的应用
8. 红黑树的封装实现STL中的map与set
8.1 红黑树的迭代器
8.2 改造红黑树
8.3 map封装红黑树的实现
8.4 set的模拟实现
8.5 其他功能的实现
红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或 Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路 径会比其他路径长出俩倍,因而是接近平衡的。
- 每个结点不是红色就是黑色
- 根节点是黑色的
- 如果一个节点是红色的,则它的两个孩子结点是黑色的
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)(也叫做NIL节点)
enum color { BLACK, RED }; template
class RBTreeNode { public: RBTreeNode * _left; RBTreeNode * _right; RBTreeNode * _parent; color _col;//节点的值域 pair _kv;//结点的颜色 RBTreeNode(const pair & kv) :_left(nullptr) , _right(nullptr) , _parent(nullptr) ,_kv(kv) {} };
问题一:红黑树是如何控制最长路径节点个数不会超过最短路径的二倍?
答:路径最长状态:一黑一红
路径极限最短状态·:全是黑(不会出现)
因为每条路径黑色节点数相同,因此极限状态下,最长路径节点数不会超过最短路径的二倍!!!
问题二:新插入节点的颜色应该选用黑色还是红色
答:如上图,如果新插入节点节点为黑色,则违反性质4,即每条路径节点颜色为黑色的数量相同!
- 插入黑色,明显是抗衡性质4,需要对每条路径,每个节点遍历,找出最短路径,且调整困难
- 插入红色,明显是抗衡性质3,仅需通过判断父节点是否为红色,为红色则需要调整,为黑色,则直接插入成功
【总结】:
红黑树主要是通过颜色使其保持平衡,且最长路径不超过最短路径的二倍
红黑树插入调整主要是变色+旋转,红黑树没有AVL树那么直观,AVL树通过平衡因子即可判断其控制方向而红黑树是通过观察颜色的规则,只要控制颜色规则,其就间接控制了最长路径就不会超过最短路径的二倍!
如上图,因为新插入节点为红色,而其parent也为红色,将parent变黑,导致了增加黑节点,性质4被破坏,而parent节点为红色,其grandfather必定为黑,将grandfather变红,也 需要控制parent的uncle为黑色,才能保持性质4被调整正确!!!
【注】在满足条件时,层层上调,保证了其性质4不会被破坏,针对其他情况,提出其他解决方案
调整完毕后,进而在加cur = grandfather,层层转换,最终,将cur变为黑色便可调增完成。
在分析1中,可能会面临uncle为黑色节点,此时变色便被终止,解决方法就是通过旋转方式+变色得到正确的二叉平衡搜索树
但是上述调节过程层层转换可能会面临:
- 情况一:uncle为空,或者uncle的颜色为黑色
- 情况二:不断处理过程中,cur的parent为黑色,此树便处理完成,如下图
- 情况三:cur走到根节点,此时cur为所有路径的根,且cur为红色,只需要将cur颜色变为黑色,此树便处理完成
通过上述分析得出以下判决条件:
1. 新插入节点为根,跟为黑色;
2. 新插入节点的parent为红色,则又分为以下判决条件:
条件一:parent为红色,其uncle也为红色,层层上调(变色)
条件二:上调过程中遇到uncle为空,或则uncle颜色为黑色(变色+旋转)
条件三:上调过程中cur的parent为黑色,处理完成
条件四:上调cur到根节点,即parent为空,此树处理完成
3. 层层调整走完,最后将根节点的颜色变为黑色(对于其他情况根节点的统一处理没有任何影响)
4. 新插入节点的parent为黑色,则不做处理
因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何 性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连 在一起的红色节点,此时需要对红黑树分情况来讨论:
约定:cur为当前节点,p为parent父节点,g为grandfather祖父节点,u为uncle叔叔节点
【注意】此处所看到的树,可能是一颗完整的树,也可能是一颗子树
上图为层层处理的具象图,及分析
解决方式:将p,u改为黑,g改为红,然后把g当成cur,继续向上调整。
说明: u的情况有两种
- 如果u节点不存在,则cur一定是新插入节点,因为如果cur不是新插入节点,则cur和p一定有一个节点的颜色是黑色,就不满足性质4:每条路径黑色节点个数相同。
- 如果u节点存在,则其一定是黑色的,那么cur节点原来的颜色一定是黑色的。现在看到其是红色的原因是因为cur的子树在调整的过程中将cur节点的颜色由黑色改成红色。
根据上述两种情况分别分析:
主要有以下四种情况
1.parent == grandfather->_left && cur = parent->_left,以grandfather为轴点,进行右单旋
2.parent == grandfather->_right && cur = parent->_right,以grandfather为轴点,进行左单旋
3.parent == grandfather->_left && cur = parent->_right,双旋 + 变色,以parent为轴点,进行左单旋,以grandfather为轴点,进行右单旋
4. parent == grandfather->_right && cur = parent->_left,双旋 + 变色,以parent为轴点,进行右单旋,以grandfather为轴点,进行左单旋
上述是uncle不存在的情况下,abcde必是空树 ,分别的处理措施
下图皆为具象图,包含多种情况,统一处理得来,cur原本是黑的,但是由于a或者b得新增,层层上调,导致其cur变红,且遇到uncle存在且为黑。
主要有以下四种情况:
1.parent == grandfather->_left && cur = parent->_left,以grandfather为轴点,进行右单旋
2.parent == grandfather->_right && cur = parent->_right,以grandfather为轴点,进行左单旋
3.parent == grandfather->_left && cur = parent->_right,双旋 + 变色,以parent为轴点,进行左单旋,以grandfather为轴点,进行右单旋
4. parent == grandfather->_right && cur = parent->_left,双旋 + 变色,以parent为轴点,进行右单旋,以grandfather为轴点,进行左单旋
上述是uncle存在的情况下,abcde可能不是空树 ,分别的处理措施
插入具体代码实现:
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 (kv.first > cur->_kv.first) { parent = cur; cur = cur->_right; } else if (kv.first < cur->_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; //cur为红色,且parent也为红色,违反性质3 //if (parent->_col == RED) { // Node* grandfather; // Node* uncle; // while (cur != _root) { // if (cur->_parent->_col == BLACK) { // return true; // } // parent = cur->_parent; // grandfather = parent->_parent; // uncle = grandfather->_left; // if (parent == grandfather->_left) { // uncle = grandfather->_right; // } // if (uncle == nullptr || uncle->_col == BLACK) { // if (grandfather->_left == parent && parent->_left == cur) { // //右旋 // RotateR(grandfather); // } // else if (grandfather->_right == parent && parent->_right == cur) { // RotateL(grandfather); // } // else if (grandfather->_left == parent && parent->_right == cur) { // RotateLR(cur); // } // else if (grandfather->_right == parent && parent->_left == cur) { // RotateRL(cur); // } // else { // assert(false); // } // return true; // } // parent->_col = BLACK; // uncle->_col = BLACK; // grandfather->_col = RED; // cur = grandfather; // } // if(cur == _root){ // cur->_col = BLACK; // } //} ///cur为红色,且parent也为黑色,返回true while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; assert(grandfather && grandfather->_col == BLACK); Node* uncle = grandfather->_left; if (parent == grandfather->_left) { uncle = grandfather->_right; } if (uncle && uncle->_col == RED) { parent->_col = uncle->_col = BLACK; grandfather->_col = RED; //继续往上处理 cur = grandfather; parent = cur->_parent; } //情况二:uncle不存在 || 存在且为黑 else { if (grandfather->_left == parent && parent->_left == cur) { RotateR(grandfather); } else if (grandfather->_right == parent && parent->_right == cur) { RotateL(grandfather); } else if (grandfather->_left == parent && parent->_right == cur) { RotateLR(cur); } else if (grandfather->_right == parent && parent->_left == cur) { RotateRL(cur); } else { assert(false); } return true; } } _root->_col = BLACK; return true; }
旋转代码实现:
void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; subR->_left = parent; Node* pParent = parent->_parent; if (parent == _root) { _root = subR; } else { if (parent == pParent->_left) { pParent->_left = subR; } else { pParent->_right = subR; } } subR->_parent = pParent; parent->_parent = subR; parent->_right = subRL; if (subRL) { subRL->_parent = parent; } parent->_col = RED; subR->_col = BLACK; } void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; subL->_right = parent; Node* pParent = parent->_parent; if (parent == _root) { _root = subL; } else { if (parent == pParent->_left) { pParent->_left = subL; } else { pParent->_right = subL; } } subL->_parent = pParent; parent->_parent = subL; parent->_left = subLR; if (subLR) { subLR->_parent = parent; } parent->_col = RED; subL->_col = BLACK; } void RotateLR(Node* cur) { Node* parent = cur->_parent; Node* grandfather = parent->_parent; RotateL(parent); RotateR(grandfather); cur->_col = BLACK; grandfather->_col = RED; } void RotateRL(Node* cur) { Node* parent = cur->_parent; Node* grandfather = parent->_parent; RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; }
红黑树的检测分为两步:
- 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
- 检测其是否满足红黑树的性质(性质2、性质3、性质4)
检测性质4以某条路径黑色节点数量作为基准值,或者传引用,记录第一条路径黑色节点数量,进而,遍历每条路径
public: bool IsBalance() { if (_root == nullptr) { return true; } if (_root->_col == RED) { cout << "性质2:根节点不是黑色" << endl; return false; } //黑色节点基准值 int benchmark = 0; Node* cur = _root; while (cur) { if (cur->_col == BLACK) { ++benchmark; } cur = cur->_left; } return _PrevCheck(_root, 0, benchmark); } private: bool _PrevCheck(Node* root, int blackNum, int benchmark) { if (root == nullptr) { if (blackNum != benchmark) { cout << "性质4:某条路径黑色节点数量不相等" << endl; return false; } else { return true; } } if (root->_col == BLACK) { ++blackNum; } if (root->_col == RED && root->_parent->_col == RED) { cout << "性质3:存在连续红色节点" << endl; return false; } return _PrevCheck(root->_left, blackNum, benchmark) && _PrevCheck(root->_right, blackNum, benchmark); }
可参考:《算法导论》或者《STL源码剖析》
红黑树 - _Never_ - 博客园 (cnblogs.com)
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O($log_2 N$),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数, 所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
C++ STL库 -- map/set、mutil_map/mutil_set
Java 库
linux内核
其他一些库
STL源码分析,对map、set及tree进行分析(只分析其结构及封装形式):
由上述分析可以观察的出map、set底层对红黑树的封装,迭代器也采用了红黑树迭代器的实现
在STL中为了后续实现关联式容器简单,更好的实现迭代器,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft 域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:
迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明。如果想要给红黑树增加迭代器,需要考虑以前问题:
begin()与end()STL中的实现:
STL明确规定,begin()与end()代表的是一段前闭后开的区间,而对红黑树进行中序遍历后, 可以得到一个有序的序列,因此:begin()可以放在红黑树中最小节点(即最左侧节点)的位 置,end()放在最大节点(最右侧节点)的下一个位置,关键是最大节点的下一个位置在哪块? 能否给成nullptr呢?答案是行不通的,因为对end()位置的迭代器进行--操作,必须要能找最 后一个元素,此处就不行,因此最好的方式是将end()放在头结点的位置。
begin()与end()不采用新增头节点实现:
如果不采用STL中的新增头节点的实现方式,其也可以通过遍历的方式线,end()指向nullptr,在迭代器操++、--作中,通过条件判断,便可依次遍历每个节点。
// 因为关联式容器中存储的是
的键值对,因此 // k为key的类型, // ValueType: 如果是map,则为pair ; 如果是set,则为k // KeyOfValue: 通过value来获取key的一个仿函数类 enum color { BLACK, RED }; template class RBTreeNode { public: RBTreeNode * _left; RBTreeNode * _right; RBTreeNode * _parent; T _data; color _col; RBTreeNode(const T& data) :_left(nullptr) , _right(nullptr) , _parent(nullptr) , _col(RED) , _data(data) {} }; template struct __RBTree_Iterator { typedef RBTreeNode Node; typedef __RBTree_Iterator Self; Node* _node; __RBTree_Iterator(Node* node) :_node(node) {} Ref operator*() { return _node->_data; } Ptr operator->() { return &(_node->_data); } Self& operator++() { if (_node->_right) { //找右树最左节点 _node = _node->_right; while (_node && _node->_left) { _node = _node->_left; } } else { Node* parent = _node->_parent; //找孩子不是父亲右的祖先 while (parent && parent->_right == _node) { _node = _node->_parent; parent = _node->_parent; } _node = parent; } return *this; } Self operator++(int) { Self prevIterator = Self(_node); if (_node->_right) { //找右树最左节点 _node = _node->_right; while (_node && _node->_left) { _node = _node->_left; } } else { Node* parent = _node->_parent; //找孩子不是父亲右的祖先 while (parent && parent->_right == _node) { _node = _node->_parent; parent = _node->_parent; } _node = parent; } return prevIterator; } Self& operator--() { if (_node->_left) { _node = _node->_left; while (_node && _node->_right) { _node = _node->_right; } } else { Node* parent = _node->_parent; while (parent && parent->_left == _node) { _node = parent; parent = _node->_parent; } _node = parent; } return *this; } Self operator--(int) { Self prevIterator = Self(_node); if (_node->_left) { _node = _node->_left; while (_node && _node->_right) { _node = _node->_right; } } else { Node* parent = _node->_parent; while (parent && parent->_left == _node) { _node = parent; parent = _node->_parent; } _node = parent; } return prevIterator; } bool operator!=(const Self& s) const { return _node != s._node; } bool operator==(const Self& s) const { return _node == s._node; } }; template class RBTree { typedef RBTreeNode Node; public: typedef __RBTree_Iterator iterator; typedef __RBTree_Iterator const_iterator; typedef __RBTree_Iterator reverse_iterator; typedef __RBTree_Iterator const_reverse_iterator; iterator begin() { Node* left = _root; while (left && left->_left) { left = left->_left; } return iterator(left); } iterator end() { return iterator(nullptr); } const_iterator begin() const { Node* left = _root; while (left && left->_left) { left = left->_left; } return const_iterator(left); } const_iterator end() const { return const_iterator(nullptr); } reverse_iterator rbegin() { Node* right = _root; while (right && right->_right) { right = right->_right; } return reverse_iterator(right); } reverse_iterator rend() { return reverse_iterator(nullptr); } pair Insert(const T& data) { if (_root == nullptr) { _root = new Node(data); _root->_col = BLACK; return std::make_pair(iterator(_root), true); } Node* parent = nullptr; Node* cur = _root; while (cur) { if (_compare(_kot(cur->_data), _kot(data))) { parent = cur; cur = cur->_right; } else if (_compare(_kot(data), _kot(cur->_data))) { parent = cur; cur = cur->_left; } else { return std::make_pair(iterator(cur), true); } } Node* newnode = new Node(data); cur = newnode; cur->_col = RED; if (_compare(_kot(parent->_data), _kot(data))) { parent->_right = cur; } else { parent->_left = cur; } cur->_parent = parent; ///cur为红色,且parent也为黑色,返回true while (parent && parent->_col == RED) { Node* grandfather = parent->_parent; assert(grandfather && grandfather->_col == BLACK); Node* uncle = grandfather->_left; if (parent == grandfather->_left) { uncle = grandfather->_right; } if (uncle && uncle->_col == RED) { parent->_col = uncle->_col = BLACK; grandfather->_col = RED; //继续往上处理 cur = grandfather; parent = cur->_parent; } //情况二:uncle不存在 || 存在且为黑 else { if (grandfather->_left == parent && parent->_left == cur) { RotateR(grandfather); } else if (grandfather->_right == parent && parent->_right == cur) { RotateL(grandfather); } else if (grandfather->_left == parent && parent->_right == cur) { RotateLR(cur); } else if (grandfather->_right == parent && parent->_left == cur) { RotateRL(cur); } else { assert(false); } return std::make_pair(iterator(newnode), true); } } _root->_col = BLACK; return std::make_pair(iterator(newnode), true); } void Inorder() { _Inorder(_root); } bool IsBalance() { if (_root == nullptr) { return true; } if (_root->_col == RED) { cout << "性质2:根节点不是黑色" << endl; return false; } //黑色节点基准值 int benchmark = 0; Node* cur = _root; while (cur) { if (cur->_col == BLACK) { ++benchmark; } cur = cur->_left; } return _PrevCheck(_root, 0, benchmark); } private: bool _PrevCheck(Node* root, int blackNum, int benchmark) { if (root == nullptr) { if (blackNum != benchmark) { cout << "性质4:某条路径黑色节点数量不相等" << endl; return false; } else { return true; } } if (root->_col == BLACK) { ++blackNum; } if (root->_col == RED && root->_parent->_col == RED) { cout << "性质3:存在连续红色节点" << endl; return false; } return _PrevCheck(root->_left, blackNum, benchmark) && _PrevCheck(root->_right, blackNum, benchmark); } void _Inorder(Node* cur) { std::stack st; while (cur || !st.empty()) { while (cur) { st.push(cur); cur = cur->_left; } cur = st.top(); std::cout << _kot(cur->_data) << " "; st.pop(); cur = cur->_right; } std::cout << std::endl; } void RotateL(Node* parent) { Node* subR = parent->_right; Node* subRL = subR->_left; subR->_left = parent; Node* pParent = parent->_parent; if (parent == _root) { _root = subR; } else { if (parent == pParent->_left) { pParent->_left = subR; } else { pParent->_right = subR; } } subR->_parent = pParent; parent->_parent = subR; parent->_right = subRL; if (subRL) { subRL->_parent = parent; } parent->_col = RED; subR->_col = BLACK; } void RotateR(Node* parent) { Node* subL = parent->_left; Node* subLR = subL->_right; subL->_right = parent; Node* pParent = parent->_parent; if (parent == _root) { _root = subL; } else { if (parent == pParent->_left) { pParent->_left = subL; } else { pParent->_right = subL; } } subL->_parent = pParent; parent->_parent = subL; parent->_left = subLR; if (subLR) { subLR->_parent = parent; } parent->_col = RED; subL->_col = BLACK; } void RotateLR(Node* cur) { Node* parent = cur->_parent; Node* grandfather = parent->_parent; RotateL(parent); RotateR(grandfather); cur->_col = BLACK; grandfather->_col = RED; } void RotateRL(Node* cur) { Node* parent = cur->_parent; Node* grandfather = parent->_parent; RotateR(parent); RotateL(grandfather); cur->_col = BLACK; grandfather->_col = RED; } protected: Compare _compare; Node* _root = nullptr; KeyOfT _kot; };
map的底层结构就是红黑树,因此在map中直接封装一棵红黑树,然后将其接口包装下即可
#pragma once #include"RBTree.h" namespace Thb { template
> class map { typedef Key key_type; typedef pair value_type; struct MapKeyOfT { const Key& operator()(const value_type& kval) { return kval.first; } }; public: typedef typename RBTree ::iterator iterator; typedef typename RBTree ::reverse_iterator reverse_iterator; iterator begin() { return _t.begin(); } iterator end() { return _t.end(); } reverse_iterator rbegin() { return _t.rbegin(); } reverse_iterator rend() { return _t.rend(); } pair insert(const value_type& val) { return _t.Insert(val); } void inorder() { _t.Inorder(); } bool is_balance() { return _t.IsBalance(); } T& operator[](const Key& key) { pair ret = insert(std::make_pair(key, T())); return (ret.first)->second; } private: typedef RBTree rep_type; rep_type _t; }; }
set的底层为红黑树,因此只需在set内部封装一棵红黑树,即可将该容器实现出来(具体实现可参考map)。
#pragma once #include"RBTree.h" namespace Thb { template
> class set { typedef Key key_type; typedef Key value_type; struct SetKeyOfT { const Key& operator()(const Key& key) { return key; } }; public: typedef typename RBTree ::iterator iterator; typedef typename RBTree ::reverse_iterator reverse_iterator; iterator begin() { return _t.begin(); } iterator end() { return _t.end(); } reverse_iterator rbegin() { return _t.rbegin(); } reverse_iterator rend() { return _t.rend(); } pair insert(const value_type& val) { return _t.Insert(val); } void inorder() { _t.Inorder(); } bool is_balance() { return _t.IsBalance(); } private: typedef RBTree rep_type; rep_type _t; }; }
其他功能的实现可参考二叉搜索树文章,增加需要的功能(1条消息) C++进阶—二叉搜索树_IfYouHave的博客-CSDN博客