目录
一.什么是红黑树
二.红黑树的性质
三.红黑树的实现
节点定义:
1.insert
情况一: cur为红,p为红,g为黑,u存在且为红
情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑
情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑
判断平衡
高度
迭代器
四,红黑树的封装
map的封装
set封装
一.什么是红黑树
红黑树(英文:Red–black tree)是一种自平衡二叉查找树,在计算机科学中被广泛应用,特别用于实现关联数组。它的名字源于1978年由利奧尼達斯·J·吉巴斯和罗伯特·塞奇威克所提出的红黑树概念。
与AVL树不同,红黑树在每个节点上增加一个存储位表示节点的颜色(可以是RED或BLACK),通过多任意一条从根到叶子的路径上节点着色方式的限制,红黑树以确保没有一条路径会比其他路径长出两倍,因此是平衡的。相对于AVL树的严格平衡,红黑树近似平衡,但相对在建树的代价时较小一点。
如下就是一个红黑树:
二.红黑树的性质
我们大概可以用五点来总结红黑树的性质:
红黑树的性质1. 每个结点不是红色就是黑色2. 根节点是黑色的3. 如果一个节点是红色的,则它的两个孩子结点是黑色的4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点5. 每个叶子结点都是黑色的 ( 此处的叶子结点指的是空结点 )
总结来说,一个红黑树首先节点只有红黑其中一种颜色,根必须时黑的,不能有连续红节点,每条路径有相同的黑节点的个数,空叶子为黑。
对于第五条,此处的叶子节点不是我们平常说的叶子节点,而是再往下一层的空节点为叶子节点,且必须为黑。
那么为什么满足上述五条,最长的子路径就不会超过最短子路径的二倍呢?
由上述特点可知,形成的最短路径时,当节点只有黑时,最短,形成的树最为平衡。
形成最长路径时,节点此时应该为一黑一红交替,此时路径应是最长。
但是由于红节点一定比黑节点少,此时最长的一定小于最短的二倍。
可以看到上图,全是黑时,路径最长最短都是3,一黑一红时,最短4,最长7,但不会比最短2倍长。
其次面对红黑树时,我们一定要弄清它的路径的个数(注意空叶子节点):
这里一共是有6条路径的。
三.红黑树的实现
节点定义:
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;enum colour _col;RBTreeNode(const pair<K,V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_col(RED){}
};
template<class K, class V>struct RBTree
{
//.......Node*_root;
}
1.insert
首先对于插入,与AVL树一样我们需要对插入之后的二叉树平衡,那么我们该如何去平衡呢?
对于红黑树是通过变色或者以及旋转来达到平衡。
我们再仔细来划分:
检测新节点插入后,红黑树的性质是否造到破坏
情况一: cur为红,p为红,g为黑,u存在且为红
那么如何调整呢?
这里我们列举一下当五个抽象子树为一个结点的情况,新增节点插入的演示:
情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑
情况一的uncle存在,那么当不存在时,或者颜色与情况一相反,就为情况二:
那么对于情况二,的解决方案是:
如果uncle是黑色:
和情况一思想一样是一(让cur节点的上面两个变黑,g变红),但是到cur这里需要旋转,
p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反, p为g的右孩子,cur为p的右孩子,则进行左单旋转,之后p、g变色--p变黑,g变红
这里我们以左左为例
情况三: cur为红,p为红,g为黑,u不存在/u存在且为黑
情况三看着和情况二一样,实则不然,这里的左右方向是不一样的。
对于他们处理的方法,还是利用旋转处理转换成情况二。
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;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}/*先一个个插入,再平衡先从情况一开始当父亲节点的颜色为红色时,需要处理*/while (parent && parent->_col == RED){Node* grandparent = parent->_parent;if (parent == grandparent->_left){Node* uncle = grandparent->_right;if (uncle && uncle->_col == RED){/*变色处理*/parent->_col = BLACK;uncle->_col = BLACK;grandparent->_col = RED;/*继续向上更新*/cur = grandparent;parent = cur->_parent;}else{/*情况二,叔叔不存在或者为黑*/if (cur == parent->_left){/*旋转加变色右单旋*/RotateR(grandparent);parent->_col = BLACK;grandparent->_col = RED;}else{/*cur为parent的right,这里归类到情况三*//*我们不再转换到情况二,直接双旋如果cur是parent的右,左单旋,之后右单旋,在变色(和情况二一样)*/RotateL(parent);RotateR(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}else {/*此时的叔叔在grandparent的左边*/Node* uncle = grandparent->_left;/*还是情况一,变色*/if (uncle && uncle->_col == RED){/*变色处理*/parent->_col = BLACK;uncle->_col = BLACK;grandparent->_col = RED;/*继续向上更新*/cur = grandparent;parent = cur->_parent;}else{/*叔叔不存在,或者存在且为黑 情况二旋转加变色处理*/if (cur == parent->_right){/*旋转加变色左单旋*/RotateL(grandparent);parent->_col = BLACK;grandparent->_col = RED;}else{/*情况三 双旋加变色处理*/RotateR(parent);RotateL(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}}/*最终_root的颜色一定为黑*/_root->_col = BLACK;return true;}
判断平衡
bool check(Node* root, int blacknum, const int flag){if (root == nullptr){if (blacknum != flag){cout << "存在黑色节点数量不相等的路径" << endl;return false;}return true;}if (root->_col == RED && root->_parent->_col == RED){cout << "有连续的红色节点" << endl;return false;}if (root->_col == BLACK){++blacknum;}return check(root->_left, blacknum, flag)&& check(root->_right, blacknum, flag);}bool is_Balance(){if (_root == nullptr)return true;if (_root->_col == RED)return false;//参考值int flag = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK){++flag;}cur = cur->_left;}int blacknum = 0;return check(_root, blacknum, flag);}
高度
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;}
迭代器
和我们之前实现的链表的迭代器类似,对于二叉树,迭代器本质就是节点指针,其次主要就是++的重载,那么如何去找中序遍历的下一个节点?
对于it指向的节点,右子树不为空,下一个就是右子树的最左节点。
对于it指向的节点,右子树为空,那说明it节点所在的子树都已经访问完了,往上找(孩子是父亲左边的)那个祖先。
这里主要是实现++功能与减减--
//迭代器
//首先迭代器是一个树的节点指针
template<class T>struct __Treeiterator {typedef RBTreeNode<T> Node;typedef __Treeiterator<T> self;Node* _node;__Treeiterator(Node*newnode):_node(newnode){}T& operator*(){return _node->_data;}T*operator->(){return &(_node->_data);}bool operator!=(const self&s){return _node != s._node;}bool operator=(const self& s){return _node = s->_node;}self& operator++(){if (_node->_right){Node*cur = _node->_right;while (cur->_left){cur = cur->_left;}_node = cur;}else{//左为空,找孩子是父亲左边的祖先Node* cur = _node;Node* parent = cur->_parent;while (parent&&cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}self& operator++(int){//先保存thisself tmp(*this);if (_node->_right){Node* cur = _node->_right;while (cur->_left){cur = cur->_left;}_node = cur;}else{//左为空,找孩子是父亲左边的祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return tmp;}elf& operator--(){//如果左节点不为空if (_node->_left){Node* cur = _node->_left;while (cur->_left){cur = cur->_left;}_node = cur;}else{//左为空,找的祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){//如果有为空,则我就是父亲cur = parent;//更新节点为parent的父亲parent = parent->_parent;}_node = parent;}return *this;}
};
四,红黑树的封装
对于红黑树的封装,就是利用用红黑树封装出set,map.,接口实现基本没变,只是对模板参数,以及类型,返回值统一一下。封装时,采用统一参数与类型模板。
封装后的RBTree的结构变化,以及insert的变化:
enum colour
{RED,BLACK
};
//不再使用k,v表示key与val的类型,直接用pair作为类型参数传入
template<class T>struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode< T>* _right;RBTreeNode<T>* _parent;T _data;enum colour _col;RBTreeNode(const T& data):_left(nullptr),_right(nullptr),_parent(nullptr),_data(data),_col(RED){}
};//增加一个模板参数,来挥去对应的key的类型
template<class K, class T, class keyofT>struct RBTree
{typedef RBTreeNode<T> Node;typedef __Treeiterator<T> iterator;iterator begin(){//begin就是这棵树的最左节点Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return iterator(cur);};iterator end(){// end就是这棵树的最右节点的下一个null,结束标志return iterator(nullptr);};
//...............................
pair<iterator, bool> insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(iterator(_root), true);}Node* parent = nullptr;Node* cur = _root;keyofT k;//一个类型while (cur){if (k(cur->_data)< k(data))//把data中的类型取出来{parent = cur;cur = cur->_right;}else if (k(cur->_data) > k(data)){parent = cur;cur = cur->_left;}else{return make_pair(iterator(_root), false);}}cur = new Node(data);cur->_col = RED;//新节点我们默认为红色,由红黑树的性质可知,若为黑节点,//每一条路径的黑色节点都要增加。/*不过新增红色节点需要去变父亲的色*/if (k(parent->_data) < k(data)){parent->_right = cur;cur->_parent = parent;}else{parent->_left = cur;cur->_parent = parent;}/*先一个个插入,再平衡先从情况一开始当父亲节点的颜色为红色时,需要处理*/while (parent && parent->_col == RED){Node* grandparent = parent->_parent;if (parent == grandparent->_left){Node* uncle = grandparent->_right;if (uncle && uncle->_col == RED){/*变色处理*/parent->_col = BLACK;uncle->_col = BLACK;grandparent->_col = RED;/*继续向上更新*/cur = grandparent;parent = cur->_parent;}else{/*情况二,叔叔不存在或者为黑*/if (cur == parent->_left){/*旋转加变色右单旋*/RotateR(grandparent);parent->_col = BLACK;grandparent->_col = RED;}else{/*cur为parent的right,这里归类到情况三*//*我们不再转换到情况二,直接双旋如果cur是parent的右,左单旋,之后右单旋,在变色(和情况二一样)*/RotateL(parent);RotateR(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}else {/*此时的叔叔在grandparent的左边*/Node* uncle = grandparent->_left;/*还是情况一,变色*/if (uncle && uncle->_col == RED){/*变色处理*/parent->_col = BLACK;uncle->_col = BLACK;grandparent->_col = RED;/*继续向上更新*/cur = grandparent;parent = cur->_parent;}else{/*叔叔不存在,或者存在且为黑 情况二旋转加变色处理*/if (cur == parent->_right){/*旋转加变色左单旋*/RotateL(grandparent);parent->_col = BLACK;grandparent->_col = RED;}else{/*情况三 双旋加变色处理*/RotateR(parent);RotateL(grandparent);cur->_col = BLACK;grandparent->_col = RED;}break;}}}/*最终_root的颜色一定为黑*/_root->_col = BLACK;return make_pair(iterator(_root), true);}private:Node* _root=nullptr;
};
map的封装
我们对于RBTree,对于map传pair,这里是有两个值的,但是我们将pair当作一个类型参数传入,但是在比较是pair自己的比较不符合我们的要求,对于比较我们一般通过仿函数来实现这里呢我们直接增加一个模板参数表示 map的 T类型。
namespace space
{template<class K, class V> struct map{//通过仿函数确定pair中我们需要的数据,即pair中的firststruct mapKeyofT{const K& operator()(const pair<K, V>& key){return key.first;}};typename typedef RBTree<K, pair<K, V>, mapkeyofT>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}pair<iterator, bool> insert(const pair<K,V>& key){return _t.insert(key);}private:RBTree<K, pair<K, V>, mapkeyofT> _t;//通过这种方式传递我们key的类型};
}
set封装
set这里的setoft只是为了是大家同意。
namespace space
{template<class K> struct set{public://对于set里的仿函数意思一下,你给我这个类型我返回你这个类型struct setKeyofT{const K& operator()(const K&key ){return key;}};typename typedef RBTree<K, K, setKeyofT>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}pair<iterator,bool> insert(const K& key){return _t.insert(key);}private:RBTree<K, K, setKeyofT> _t;};
}