红黑树总结整理
红黑色概述:
红黑树整理与AVL树类似,但在对树的平衡做控制时,AVL树会比红黑树更严格。
AVL树是通过引入平衡因子的概念进行对树高度控制。
红黑树则是对每个节点标记颜色,对颜色进行控制。
红黑树控制规则:
1.每个节点不是红色就是黑色
2.根节点必须是黑色
3.如果一个节点是红色,那么此节点的左右孩子节点必须是黑色。也就是说在一条路径上,没有连续的红色节点。
4.对任意一个节点,从该节点到其所有的NULL节点上路径的黑色节点数量均相等。
这样组合的特性是:树的最长路径不会超过最短路径的两倍。
最长路径<=2*最短路径
红黑树保证效率的方式就是遵守红黑树的4条规则。
已知红黑树从根节点到任意路径上的NULL节点中都拥有相同数量的黑色节点。
在极端情况下,一条路径全色黑色节点,我们称之为最短路径用bh表示。
又根据规则3,一个红节点它的左右两个孩子就必须是黑色节点,可以反推,一个红色节点的父亲节点是一个黑色节点。所以最长路径的组成就变成了间隔相邻的红黑节点,共有2bh个。
所以结论就为:
红黑树的高度差控制在bh<=h<=2bh
红黑树的效率:
红黑树的效率与AVL树相差无几,假设 N为红黑树的节点数量,h为最短路径。
所以求h就等于log以2为底的N。效率也就是logN级别
红黑树的插入:
红黑树的插入必须遵守四个规则。在插入空树时,根节点为黑色。而在插入非空树时,新增节点的初始颜色必须为红色。这是因为若初始颜色为黑色,会导致某一路径的黑色节点数量多于其他路径,违反规则4,并且很难维护。因此,插入时新节点的颜色应始终为红色。
当我们新插入节点为红色时,我们检查父节点,如果父节点为黑色,那么直接完成插入,如果父节点为红色,就违反了规则3,需要进行特殊处理。
先将新插入的节点标记为 c(cur),那么它的父亲为 p(parent) ,它的爷爷为g(grandfather)它的爷爷绝对是黑色节点,如果不为黑色,那么这颗红黑树早就违反了规则3.
并且还需要g节点找到并标记u(uncle)节点,也就是和p同级的兄弟节点。
情况一:变色
情况二:单旋+变色
c,p节点都为红色,那么u节点除了红色就只有存在且为黑色或不存在两种情况。
如果u节点不存在,那么c是新增节点
如果u存在且为黑色,那么c节点绝对不是叶子节点,c节点下还有子树。只不过是c节点的子树变化导致c变成了红色。
那么如何验证这个结论呢?
观察上图,如果u有节点且为黑色的话,此时就以及违反了规则4,所有路径的黑色节点数量不对等。
当u存在且为黑色的时候,C必然不是新增节点,它只可能是a或b子树里进行变换后当值c变成了红色。所以当我们在进行颜色变化的时候,需要循环向上变化。只有当p节点为黑色才停止。
u不存在或u存在且为黑色时候,我们单纯变色是不行的
会发现,当我们仅仅只是变色的话,就会违反规则4,根到左路径会比根到右路径的黑色节点个数多一。
当我们u为空时候,就需要进行一次右旋,让子树达到平衡后更新p节点为黑色,g节点为红色就能确保4条规则都不触犯,并且因为p已经变成了黑色,就不需要再往上跟新节点。
当u不为空时,c子树必然有黑色节点。同样的我们进行右旋后,将p变为黑色,g变成红色后还是遵守着4条规则。
这里也仅仅介绍右旋的情况,如果是在左边的话就同理进行左旋加变色,
情况三:双选+变色
c节点也不会一直如我们所愿,只插入在绝对的左边或绝对的右边,这时候就需要经过两次旋转,才能完成红黑树的变化。
当c在p的右边的时候就必须要通过双选,当c转完两次的时候,c来到最上层,此时将c节点变为黑色,将g节点变为红色,c又恰好为上层节点,就可以直接退出循环。
当u不为空且为黑色时
同样的,u为黑色那么c节点肯定不是新插入的节点,c原本为黑色节点。因为新插入的节点在c的子树内,导致子树的变化影响了C节点从黑色变为红色才发生的双选+变色。
最后戳了c在左子树,c的情况也会在右子树出现,所实现的方法也是也要的,并不需要特别说明举例。
以下是红黑树的代码
#pragma once
#include<iostream>
using namespace std;
enum Color { red ,black };template<class K, class V>
struct TreeNode
{pair<K, V> _val;TreeNode* _left;TreeNode* _right;TreeNode* _parent;Color _col;TreeNode(const pair<K, V>& val):_val(val),_left(nullptr),_right(nullptr),_parent(nullptr),_col(red){}
};template<class K,class V>
class RBTree
{typedef TreeNode<K, V> Node;
public:bool Insert(const pair<K,V> &val){if(_root==nullptr){_root = new Node(val);_root->_col = black;return true;}Node* parent = nullptr;Node* cur = _root;while (cur){parent = cur;if(cur->_val.first>val.first){cur = cur->_left;}else if (cur->_val.first < val.first){cur = cur->_right;}else{return false;}}cur = new Node(val);if (parent->_val.first>cur->_val.first){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;//判断红黑树是否要更新while (parent&&parent->_col!=black){Node* grandfather = parent->_parent;Node* uncle;if (grandfather->_left==parent){uncle = grandfather->_right;if (uncle&&uncle->_col==red)//情况一:只变色{grandfather->_col = red;uncle->_col = parent->_col = black;cur = grandfather;parent = cur->_parent;}else{if (parent->_left==cur)//情况二:单旋+变色{TurnR(grandfather);grandfather->_col = red;parent->_col = black;break;}else//情况三:双循+变色{TurnL(parent);TurnR(grandfather);cur->_col = black;grandfather->_col = red;break;}}}else{uncle = grandfather->_left;if (uncle && uncle->_col == red)//情况一:只变色{grandfather->_col = red;uncle->_col = parent->_col = black;cur = grandfather;parent = cur->_parent;}else{if (parent->_right == cur)//情况二:单旋+变色{TurnL(grandfather);grandfather->_col = red;parent->_col = black;break;}else//情况三:双循+变色{TurnR(parent);TurnL(grandfather);cur->_col = black;grandfather->_col = red;break;}}}}_root->_col = black;}bool Check(Node*_root,int ibnum,const int rnum){if (_root==nullptr){if (ibnum != rnum) {cout << "存在黑色结点的数量不相等的路径" << endl;return false;}return true;}if (_root->_col==red&&_root->_parent->_col==red){cout << _root->_val.first<< "存在连续的红色结点" << endl;return false;}if (_root->_col==black){++ibnum;}return Check(_root->_left, ibnum, rnum) && Check(_root->_right, ibnum, rnum);}bool IsBalance(){if (_root==nullptr||_root->_col==red){return false;}int rnum = 0;Node* cur = _root;while (cur){if (cur->_col == black)rnum++;cur = cur->_left;}return Check(_root, 0, rnum);}// blackNum 根到当前结点的黑色结点的数量//bool Check(Node* root, int blackNum, const int refNum)//{// if (root == nullptr)// {// // 前序遍历走到空时,意味着一条路径走完了// //cout << blackNum << endl;// if (refNum != blackNum)// {// cout << "存在黑色结点的数量不相等的路径" << endl;// return false;// }// return true;// }// // 检查孩子不太方便,因为孩子有两个,且不一定存在,反过来检查父亲就方便多了// if (root->_col == red && root->_parent->_col == red)// {// cout << root->_val.first << "存在连续的红色结点" << endl;// return false;// }// if (root->_col == black)// {// blackNum++;// }// return Check(root->_left, blackNum, refNum)// && Check(root->_right, blackNum, refNum);//}//bool IsBalance()//{// if (_root == nullptr)// return true;// if (_root->_col == red)// return false;// // 参考值// int refNum = 0;// Node* cur = _root;// while (cur)// {// if (cur->_col == black)// {// ++refNum;// }// cur = cur->_left;// }// return Check(_root, 0, refNum);//}void InOrder(){_InOrder(_root);}protected:void _InOrder(Node*cur){if (cur==nullptr){return;}_InOrder(cur->_left);cout << "first:" << cur->_val.first << " second:" << cur->_val.second << endl;_InOrder(cur->_right);}void TurnR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;subL->_right = parent;if (subLR){subLR->_parent = parent;}Node* Pparent = parent->_parent;parent->_parent = subL;if (Pparent){if (Pparent->_left == parent){Pparent->_left = subL;}else{Pparent->_right = subL;}}subL->_parent = parent;}void TurnL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;subR->_left = parent;if (subRL) { subRL->_parent = parent; }Node* Pparent = parent->_parent;parent->_parent = subR;if (Pparent){if (Pparent->_left == parent){Pparent->_left = subR;}else{Pparent->_right = subR;}}subR->_parent = parent;}private:Node* _root=nullptr;
};
封装map与set:
在map与set中都是对底层的红黑树进行上层的封装。
封装后的红黑树:
#pragma once
#include<iostream>
using namespace std;enum color
{red,black
};template<class T>
struct TreeNode
{TreeNode(const T&ky):_ky(ky),_col(red),_parent(nullptr),_left(nullptr),_right(nullptr){}T _ky;color _col;TreeNode* _parent;TreeNode* _left;TreeNode* _right;
};template<class T, class Ref,class Ptr >
struct Tree_Iterator
{typedef TreeNode<T> Node;typedef Tree_Iterator<T,Ref,Ptr> Self;Tree_Iterator(Node* node, Node* root):_node(node), _root(root){}Ref operator *(){return _node->_ky;}Ptr operator ->(){return &_node->_ky;}Self&operator++(){if (_node->_right){Node* minleft = _node->_right;while (minleft->_left){minleft = minleft->_left;}_node = minleft;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent&&parent->_right==cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}Self &operator --(){if (_node==nullptr){Node* cur = _root;while (cur&&cur->_right){cur = cur->_right;}_node = cur;}else if (_node->_left){Node* maxright = _node->_left;while (maxright->_right){maxright = maxright->_right;}_node = maxright;}else{Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_left == cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}bool operator !=(const Self&kv ){return _node != kv._node;}bool operator ==(const Self& kv){return _node == kv._node;}Node* _node;Node* _root;};template<class K,class T,class KeyOFT>
class RBTree
{typedef TreeNode<T> Node;
public:typedef Tree_Iterator<T,T&,T*> Iterator;typedef Tree_Iterator<T, const T&, const T*> Const_Iterator;Iterator Begin(){Node* cur = _root;while (cur&&cur->_left){cur = cur->_left;}return Iterator(cur, _root);}Iterator End(){return Iterator(nullptr, _root);}Const_Iterator CBegin()const {Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return Iterator(cur, _root);}Const_Iterator CEnd()const {return Iterator(nullptr, _root);}pair<Iterator,bool> Insert(const T&ky){if(!_root){_root = new Node(ky);_root->_col = black;return make_pair(Iterator(_root,_root),true);}Node* cur = _root;Node* parent = nullptr;KeyOFT kot;while (cur){if (kot(cur->_ky) > kot(ky)){parent = cur;cur = cur->_left;}else if (kot(cur->_ky) < kot(ky)){parent = cur;cur = cur->_right;}else{return make_pair(Iterator(cur, _root), false);;}}cur = new Node(ky);Node* node = cur;if (kot(parent->_ky)>kot(cur->_ky)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent&&parent->_col==red){Node* garendparent = parent->_parent;Node* uncle;if (garendparent->_left==parent)//只变色{uncle = garendparent->_right;if (uncle&&uncle->_col==red){uncle->_col = parent->_col = black;garendparent->_col = red;cur = garendparent;parent = cur->_parent;}else {if (parent->_left==cur)//单选变色{TurnR(garendparent);parent->_col = black;cur->_col = garendparent->_col = red;break;}else//双旋变色{TurnL(parent);TurnR(garendparent);cur->_col = black;parent->_col = garendparent->_col = red;break;}}}else{uncle = garendparent->_left;if (uncle && uncle->_col == red){uncle->_col = parent->_col = black;garendparent->_col = red;cur = garendparent;parent = cur->_parent;}else{if (parent->_right == cur)//单选变色{TurnL(garendparent);parent->_col = black;cur->_col = garendparent->_col = red;break;}else//双旋变色{TurnR(parent);TurnL(garendparent);cur->_col = black;parent->_col = garendparent->_col = red;break;}}}}_root->_col = black;return make_pair(Iterator(node, _root), true);}Node*Find(const K &val){Node* cur = _root;KeyOFT kot;while (cur){if (kot(cur->_ky) > val){cur = cur->_left;}else if (kot(cur->_ky) < val){cur = cur->_right;}else{return cur;}}return nullptr;}private:void TurnR(Node*parent){KeyOFT kot;Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if(subLR){subLR->_parent = parent;}subL->_right = parent;Node* Pparent = parent->_parent;parent->_parent = subL;if (!Pparent){subL->_parent = nullptr;_root = subL;}else{if (kot(Pparent->_ky)> kot(subL->_ky)){Pparent->_left = subL;}else{Pparent->_right = subL;}subL->_parent = Pparent;}}void TurnL(Node*parent){KeyOFT kot;Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL){subRL->_parent = parent;}subR->_left = parent;Node* Pparent = parent->_parent;parent->_parent = subR;if (!Pparent){subR->_parent = nullptr;_root = subR;}else{if (kot(Pparent->_ky) > kot(subR->_ky)){Pparent->_left = subR;}else{Pparent->_right = subR;}subR->_parent = Pparent;}}Node* _root = nullptr;
};
对于map与set在底层用的都是同一颗红黑树,只是通过类模板进行了实例化两份,所以在红黑树的自身的封装中需要改动以下措施:
1.树节点存储的值只用一个T类,因为编译器不知道要存的是Key模型,还是pair模型。
2.为红黑树添加迭代器,map与set都支持迭代器访问数据。需要重点注意的是迭代器的++与迭代器的--,这也是封装后的红黑树的重点
Self& operator++()
(前置递增)
-
当前节点有右子树:
-
找到右子树中的最左节点(即右子树中的最小节点),该节点即为当前节点的后继。
-
例如,若当前节点为
A
,右子树为C
,则后继是C
的最左子节点(假设为D
)
if (_node->_right) {Node* minleft = _node->_right;while (minleft->_left) {minleft = minleft->_left;}_node = minleft; }
-
-
当前节点无右子树:
-
从当前节点向上回溯,找到第一个祖先节点,使得当前节点位于该祖先节点的左子树中。
-
例如,若当前节点为
E
,其父节点为B
,祖父节点为A
,则A
是E
的后继。
else {Node* cur = _node;Node* parent = cur->_parent;while (parent && parent->_right == cur) {cur = parent;parent = parent->_parent;}_node = parent; // 找到的祖先节点即为后继
-
Self& operator--()
(前置递减)
-
当前节点为
nullptr
(如end()
迭代器):-
找到树中的最右节点(即最大值节点),作为前驱。
-
例如,若树为
A(B(D, E), C)
,则最右节点为C
。
if (_node == nullptr) {Node* cur = _root;while (cur && cur->_right) {cur = cur->_right;}_node = cur; // 指向最大值节点 }
-
-
当前节点有左子树:
-
找到左子树中的最右节点(即左子树中的最大节点),该节点即为当前节点的前驱。
-
例如,若当前节点为
A
,左子树为B
,则前驱是B
的最右子节点E
。
else if (_node->_left) {Node* maxright = _node->_left;while (maxright->_right) {maxright = maxright->_right;}_node = maxright; }
-
还需要注意对于为什么有KeyOFT,是因为我们红黑树在插入时需要进行比较大小,而map插入的是一个pair,而set插入的就只是Key。红黑树本身是并不知道该拿什么进行比较。所以这里传入一个KeyOFT,是上层map或set传入的一个函数模板,map就通过类模板过滤进行pair的first进行比较,set通过自身类模板比较的还是Key。
封装map:
map的封装也并没有太大难度,上述写道的类模板比较需要自己写,并且在定义成员变量红黑树的时,第二个类模板应传入的是pair,而不是V。并且map是支持运算符重载[ ],所以我们这里也同样支持,其余的也只是对红黑树的再一次调用。
#pragma once
#include"RBTree.h"namespace cat
{template<class K,class V >class mymap{struct MapOFT{const K &operator ()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapOFT>::Iterator iterator;typedef typename RBTree<K, pair<const K,const V>, MapOFT>::Const_Iterator const_iterator;pair<iterator, bool> insert(const pair<K,V> &kv){return _rbt.Insert(kv);}iterator begin(){return _rbt.Begin();}iterator end(){return _rbt.End();}const_iterator cbegin() const {return _rbt.CBegin();}const_iterator cend()const {return _rbt.CEnd();}V& operator[](const K&ky){pair<iterator, bool> ret = _rbt.Insert({ ky,V() });return ret.first->second;}private:RBTree< K, pair<const K,V>, MapOFT> _rbt;};void test_map(){mymap<int, int> m;m.insert({ 1,1 });m.insert({ 5,5 });m.insert({ 2,2 });m.insert({ 6,6 });mymap<int, int>::iterator it = m.begin();while (it != m.end()){//it->first += 1;it->second += 1;cout << it->first << ":" << it->second << endl;++it;}mymap<string, string> dict;dict.insert({ "sort", "排序" });dict.insert({ "left", "左边" });dict.insert({ "right", "右边" });dict["left"] = "左边,剩余";dict["insert"] = "插入";dict["string"];for (auto& kv : dict){cout << kv.first << " " << kv.second << endl;}cout << endl;string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜","苹果", "香蕉", "苹果", "香蕉" };mymap<string, int> countMap;for (const auto& str : arr){countMap[str]++;}for (const auto& e : countMap){cout << e.first << ":" << e.second << endl;}cout << endl;}}
封装Set
#pragma once
#include"RBTree.h"namespace cat
{template<class K>class myset{struct SetOFT{const K& operator()(const K& ky){return ky;}};public:typedef typename RBTree<K, K, SetOFT>::Iterator iterator;typedef typename RBTree<K, K, SetOFT>::Const_Iterator const_iterator;pair<iterator,bool> insert(const K& ky){return _rbt.Insert(ky);}iterator begin(){return _rbt.Begin();}iterator end(){return _rbt.End();}const_iterator begin() const {return _rbt.CBegin();}const_iterator end() const{return _rbt.CEnd();}private:RBTree<K, K, SetOFT> _rbt;};void test_set(){myset<int> s;/*s.insert(4);s.insert(1);s.insert(5);s.insert(3);*/int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){if(e==14){int x = 1;}s.insert(e);}myset<int>::iterator it = s.begin();// *it += 10;while (it != s.end()){cout << *it << " ";++it;}cout << endl;it = s.end();while (it != s.begin()){--it;cout << *it << " ";}cout << endl;}
}