C++ 改造红黑树,封装map和set
一.前言:已经实现好了的红黑树
set是Key模型的红黑树
map是Key-Value模型的红黑树
我们之前已经把红黑树实现并测试过了
这是Key-Value模型的红黑树
RBTree.h
#pragma once enum Color { RED, BLACK }; template<class K,class V> struct RBTreeNode { RBTreeNode<K,V>* _pLeft; RBTreeNode<K,V>* _pRight; RBTreeNode<K,V>* _pParent; Color _color; pair<K,V> _data; //新插入的节点默认是红色 RBTreeNode(const pair<K,V>& data) :_pLeft(nullptr) ,_pRight(nullptr) ,_pParent(nullptr) ,_color(RED) ,_data(data) {} }; template<class K,class V> class RBTree { typedef RBTreeNode<K,V> Node; public: // 在红黑树中插入值为data的节点,插入成功返回true,否则返回false // 注意:为了简单起见,本次实现红黑树不存储重复性元素 bool Insert(const pair<K,V>& data) { if (_pRoot == nullptr) { _pRoot = new Node(data); //根节点是黑色 _pRoot->_color = BLACK; return true; } Node* cur = _pRoot, * parent = nullptr; while (cur) { //比我小,往左找 if (data.first < cur->_data.first) { parent = cur; cur = cur->_pLeft; } //比我大,往右找 else if (data.first > cur->_data.first) { parent = cur; cur = cur->_pRight; } //有重复元素,插入失败 else { return false; } } //找到空位置,开始插入 cur = new Node(data); //连接父亲和孩子 if (cur->_data.first < parent->_data.first) { parent->_pLeft = cur; } else { parent->_pRight = cur; } cur->_pParent = parent; //父亲是黑色,插入成功 if (parent->_color == BLACK) { return true; } //父亲是红色,需要调整 //且此时爷爷一定是黑色 //根据叔叔的颜色来调整 while (parent && parent->_color == RED) { Node* grandParent = parent->_pParent; //爸爸是爷爷的左孩子 if (parent == grandParent->_pLeft) { Node* uncle = grandParent->_pRight; //根据叔叔的颜色来调整 //1.叔叔存在且为红 if (uncle && uncle->_color == RED) { //修改爸爸,叔叔,爷爷的颜色 uncle->_color = parent->_color = BLACK; grandParent->_color = RED; //此时视爷爷为新插入的红色节点继续向上影响 cur = grandParent; parent = cur->_pParent; } //2.叔叔不存在或者叔叔是黑 else { //1.我是爸爸的左孩子 if (parent->_pLeft == cur) { //对爷爷进行一次右旋 RotateR(grandParent); //把爷爷改成红色,爸爸改成黑色 grandParent->_color = RED; parent->_color = BLACK; //此时爸爸是黑色,无需break,当然break也可以,因此爸爸是黑色,下次循环就不会进入了 } //2.我是爸爸的右孩子 else { //1.对爸爸进行左旋 RotateL(parent); //2.对爷爷右旋 RotateR(grandParent); //3.孩子改成黑色,爷爷改成红色 cur->_color = BLACK; grandParent->_color = RED; //4.一定要break,如果不break的话,因为爸爸是红色,所以循环会继续,此时就乱套了 break; } } } //爸爸是爷爷的右孩子 else { Node* uncle = grandParent->_pLeft; //1.叔叔存在且为红 if (uncle && uncle->_color == RED) { uncle->_color = parent->_color = BLACK; grandParent->_color = RED; cur = grandParent; parent = cur->_pParent; } //2.叔叔不存在或者叔叔为黑 else { //1.我是爸爸的右孩子 if (parent->_pRight == cur) { //对爷爷左旋 RotateL(grandParent); //爷爷改成红色,爸爸改成黑色 grandParent->_color = RED; parent->_color = BLACK; //此时爸爸是黑色,无需break,当然break也可以,因此爸爸是黑色,下次循环就不会进入了 } //2.我是爸爸的左孩子 else { //1.对爸爸右旋 RotateR(parent); //2.对爷爷左旋 RotateL(grandParent); //3.把孩子改成黑色,爷爷改成红色 cur->_color = BLACK; grandParent->_color = RED; //4.一定要break,如果不break的话,因为爸爸是红色,所以循环会继续,此时就乱套了 break; } } } } _pRoot->_color = BLACK; return true; } // 检测红黑树中是否存在关键字为key的节点,存在返回该节点的地址,否则返回nullptr Node* Find(const K& key) { Node* cur = _pRoot; while (cur) { if (cur->_data.first > key) { cur = cur->_pLeft; } else if (cur->_data.second < key) { cur = cur->_pRight; } else { return cur; } } return nullptr; } // 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测 bool IsValidRBTRee() { //1.空树是红黑树 if (_pRoot == nullptr) { return true; } //2.根节点不能为红色 if (_pRoot->_color == RED) { return false; } //3.为了验证性质: 红黑树的任意一条路径上的黑色节点个数相同 找参考值 size_t ReferenceCount = 0; Node* cur = _pRoot; while (cur) { if (cur->_color == BLACK) { ReferenceCount++; } cur = cur->_pLeft; } return _IsValidRBTRee(_pRoot, 0, ReferenceCount); } void InOrder() { _InOrder(_pRoot); } private: bool _IsValidRBTRee(Node* pRoot, size_t blackCount, size_t& ReferenceCount) { if (pRoot == nullptr) { if (blackCount != ReferenceCount) { cout << "存在黑色节点数量不相等的路径" << endl; return false; } return true; } //验证性质: 红黑树中不能存在连续的红色节点 //如果一个节点是红色,该节点一定不是根节点,因此该节点一定有父亲 //只需要保证红色节点的父亲不是红色节点即可 if (pRoot->_color == RED) { if (pRoot->_pParent->_color == RED) { cout << "存在连续的红色节点" << endl; return false; } } else { blackCount++; } return _IsValidRBTRee(pRoot->_pLeft, blackCount, ReferenceCount) && _IsValidRBTRee(pRoot->_pRight, blackCount, ReferenceCount); } // 右单旋 void RotateR(Node* pParent) { Node* subL = pParent->_pLeft; Node* subLR = subL->_pRight; Node* grandParent = pParent->_pParent; subL->_pRight = pParent; pParent->_pParent = subL; pParent->_pLeft = subLR; if (subLR) subLR->_pParent = pParent; if (grandParent == nullptr) { _pRoot = subL; _pRoot->_pParent = nullptr; } else { if (pParent == grandParent->_pLeft) { grandParent->_pLeft = subL; } else { grandParent->_pRight = subL; } subL->_pParent = grandParent; } } // 左单旋 void RotateL(Node* pParent) { Node* subR = pParent->_pRight; Node* subRL = subR->_pLeft; Node* grandParent = pParent->_pParent; pParent->_pParent = subR; subR->_pLeft = pParent; pParent->_pRight = subRL; if (subRL) subRL->_pParent = pParent; //说明此时pParent是_pRoot if (grandParent == nullptr) { _pRoot = subR; _pRoot->_pParent = nullptr; } //说明此时pParent所在的树是一颗子树,需要跟父亲链接 else { if (pParent == grandParent->_pLeft) { grandParent->_pLeft = subR; } else { grandParent->_pRight = subR; } subR->_pParent = grandParent; } } void _InOrder(Node* root) { if (root == nullptr) return; _InOrder(root->_pLeft); cout << root->_data.first << " " << root->_data.second << " "; _InOrder(root->_pRight); } private: Node* _pRoot = nullptr; };
要想用红黑树来封装set和map,我们首先想到的是在搞一份Key模型的红黑树,
然后用Key模型红黑树来封装set,Key-Value模型红黑树封装map
但是STL库中set和map的设计却不是这样的
它们都是只用了一份红黑树,怎么做到的呢?
下面就让我们来探索一下
二.简化STL库里面对于map和set的封装
红黑树的源码:是一个KV模型的红黑树
它是通过传入的Value的值来判断类型,利用模板的技术实现的一棵泛型的红黑树
通过模板的实例化,实现了map和set
1.STL库中红黑树的简化代码
我们挑选出一部分很重要的成员来看看STL库中的红黑树是怎么个泛型法呢?
2.STL库中set的简化代码
下面我们看一下set
3.STL库中map的简化代码
下面我们看一下map
4.封装map和set的第一步
RBTree.h
template<class K, class V> class RBTree
对于V这个模板参数:
可能是键值Key,也可能是由Key和Value共同组成的键值对pair
MySet.h
对于set而言,传入底层红黑树的模板参数就是Key和Key
template<class K> class set { private: RBTree<K, K> _root; };
MyMap.h
对于map而言,传入底层红黑树的模板参数就是Key和pair<const Key,Value>
template<class K, class V> class map { private: //我们知道:map的Key不允许被修改,Value允许被修改 //因此pair里面的K加上const修饰 RBTree<K, pair<const K, V>> _root; };
5.红黑树第一个模板参数的价值
通过上面的传参,我们可以知道:
只需要对于红黑树的第二个模板参数传入不同的值就可以对set和map进行很好地区分
那么红黑树的第一个模板参数是不是就没有用了呢?
对于insert(const Value& v)来说,是这样的,因为插入的值就是value
set的value是key,map的value是pair
但是对于find(const Key& k)来说,查找的依据是key,对于set是可以的
但是对于map来说,它的value是一个键值对,而我们是需要key来查找的,因此第一个模板参数不能不要
6.红黑树节点的定义
enum Color { RED, BLACK }; template<class T> struct RBTreeNode { RBTreeNode<T>* _pLeft; RBTreeNode<T>* _pRight; RBTreeNode<T>* _pParent; Color _color; T _data; //新插入的节点默认是红色 RBTreeNode(const T& data) :_pLeft(nullptr) , _pRight(nullptr) , _pParent(nullptr) , _color(RED) , _data(data) {} };
三.仿函数
1.解除仿函数的误解
在学习优先级队列的时候我们遇到了仿函数
当时我们写了一个greater的仿函数,传入greater就能建小堆
传入less就能建大堆
当时我们知道:仿函数是一个类,主要重载operator()
不过仿函数不仅仅可以用于比较大小,这是我们常有的一大误解
2.仿函数在这里的价值
由于红黑树不知道传的是set还是map
因此在insert时进行节点键值的比较时,底层红黑树需要使用仿函数来获取键值key,从而才能进行比较
3.set的仿函数
对set而言,仿函数就是返回Key
template<class K> class set { public: struct SetKeyofT { const K& operator()(const K& k) { return k; } }; private: RBTree<K, K,SetKeyofT> _root; };
4.map的仿函数
对map而言,仿函数就是返回pair的first,也就是Key
template<class K, class V> class map { public: struct MapKeyofT { const K& operator()(const pair<const K, V>& kv) { return kv.first; } }; private: //我们知道:map的Key不允许被修改,Value允许被修改 //因此pair里面的K加上const修饰 RBTree<K, pair<const K, V>,MapKeyofT> _root; };
5.红黑树的修改
修改之后,因为红黑树的代码太长了,所以这里只列出修改的地方
1.类模板
template<class K, class V,class KeyofT>
2.成员变量
private: Node* _pRoot = nullptr; KeyofT _kot;
3.insert插入时的比较逻辑
//1 参数只需要传V即可(也就是红黑树的第二个模板参数) //对于set而言V就是Key //对于map而言,V就是pair<K,V> bool Insert(const V& data) { if (_pRoot == nullptr) { _pRoot = new Node(data); //根节点是黑色 _pRoot->_color = BLACK; return true; } Node* cur = _pRoot, * parent = nullptr; while (cur) { //比我小,往左找 if (_kot(data) < _kot(cur->_data)) { parent = cur; cur = cur->_pLeft; } //比我大,往右找 else if (_kot(data) > _kot(cur->_data)) { parent = cur; cur = cur->_pRight; } //有重复元素,插入失败 else { return false; } } //找到空位置,开始插入 cur = new Node(data); //连接父亲和孩子 if (_kot(cur->_data) < _kot(parent->_data)) { parent->_pLeft = cur; } //..旋转变色等其他操作,这些操作都无需进行节点的Key有关比较 }
6.仿函数小总结
四.迭代器
1.迭代器类的定义
跟list类似,红黑树的正向迭代器也是对节点指针进行了一层封装
同样的,为了实现const_iterator,这里传入Ref和Ptr这两个模板参数
不过这里增加了const迭代器和非const迭代器的转化
template<class T, class Ref, class Ptr> struct __TreeIterator { typedef RBTreeNode<T> Node; typedef __TreeIterator<T, T&, T*> iterator; typedef __TreeIterator<T, Ref, Ptr> Self; Node* _node; __TreeIterator(Node* node) :_node(node) {} //普通迭代器和const_iterator的转化 __TreeIterator(const iterator& it) :_node(it._node) {} Ref operator*(); Ptr operator->(); //找中序遍历的下一个节点 Self& operator++(); bool operator!=(const Self& s); bool operator==(const Self& s); };
2.解引用,!=,==的实现
Ref operator*() { return _node->_data; } Ptr operator->() { return &_node->_data; } bool operator!=(const Self& s) { return _node != s._node; } bool operator==(const Self& s) { return _node == s._node; }
3.operator++
这里的迭代器走的是二叉树的中序遍历,因此++要找到中序遍历的下一个节点
如何找呢?
1.如果节点的右子树不为空,中序的下一个节点就是右子树的最左节点
2.如果节点的右子树为空,那么就需要一直往上找
直到父亲为空或者孩子是父亲的左孩子时,此时的父亲就是中序的下一个节点
//找中序遍历的下一个节点 Self& operator++() { //1.左子树 根节点 右子树 //如果有右孩子,那就是右子树的最左节点 //如果没有右孩子,那往上找直到我是父亲的左孩子或者我的父亲是空节点为止,此时我的父亲就是下一个节点 Node* cur = _node; if (cur->_pRight) { Node* right = cur->_pRight; while (right->_pLeft) { right = right->_pLeft; } _node = right; } else { Node* parent = cur->_pParent; while (parent && parent->_pRight == cur) { cur = parent; parent = cur->_pParent; } _node = parent; } return *this; }
4.给红黑树加上begin和end
begin是中序的第一个节点,也就是最左节点
end是中序的最后一个节点的下一个节点,也就是空节点
跟list一样,普通迭代器就是V V& V*
const_iterator就是V const V& const V*
template<class K, class V,class KeyofT> class RBTree { typedef RBTreeNode<V> Node; public: typedef __TreeIterator<V, V&, V*> iterator; typedef __TreeIterator<V,const V&,const V*> const_iterator; iterator begin() { Node* cur = _pRoot; while (cur && cur->_pLeft) { cur = cur->_pLeft; } return iterator(cur); } iterator end() { return iterator(nullptr); } const_iterator begin() const { Node* cur = _pRoot; while (cur && cur->_pLeft) { cur = cur->_pLeft; } return const_iterator(cur); } const_iterator end() const { return const_iterator(nullptr); } //其他成员.... };
五.set的实现
1.注意
1.typename
直接复用红黑树的接口即可实现set
注意:
typedef typename RBTree<K, K, SetKeyofT>::const_iterator iterator;
typename的作用:因为没有实例化的模板是区分不了静态变量和类型的,因此需要我们使用typename告诉编译器这是类型
2.set的特性
库里面的set是不允许修改里面的值的
也就是说set的普通迭代器和const迭代器其实都是const迭代器
因此都复用红黑树的const_iterator即可
2.set的代码
namespace wzs { template<class K> class set { public: struct SetKeyofT { const K& operator()(const K& k) { return k; } }; typedef typename RBTree<K, K, SetKeyofT>::const_iterator iterator; typedef typename RBTree<K, K, SetKeyofT>::const_iterator const_iterator; iterator begin() const { return _root.begin(); } iterator end() const { return _root.end(); } pair<iterator, bool> insert(const K& k) { return _root.Insert(k); } private: RBTree<K, K, SetKeyofT> _root; }; }
六.map的实现
1.operator[]的说明
STL库里面的map容器的方括号[]的作用:
返回值: 插入成功:pair<新插入的key所在的节点的iterator,true> 插入失败:pair<已经存在的key所在的节点的iterator,false> 作用: 如果插入成功,那么operator[]相当于insert 如果插入失败,那么operator[]相当于find 也就是说operator[]有多重技能 operator[]是map的一个非常重要的函数,可以实现3个功能:插入,查找,修改
这是对源码的一个简化:
operator[]:给一个key,返回这个key所对应的value的引用 简化前: mapped_type& operator[](const Key_type& k) { return (*(this->insert(make_pair(k,mapped_type())))).first).second; } 简化后 V& operator(const K& key) { pair<iterator,bool> ret=insert(key,V());//V():value的默认构造的匿名对象 return ret.first->second;
2.map的代码
namespace wzs { template<class K, class V> class map { public: struct MapKeyofT { const K& operator()(const pair<const K, V>& kv) { return kv.first; } }; typedef typename RBTree<K, pair<const K, V>, MapKeyofT>::iterator iterator; typedef typename RBTree<K, pair<const K, V>, MapKeyofT>::const_iterator const_iterator; iterator begin() { return _root.begin(); } iterator end() { return _root.end(); } const_iterator begin() const { return _root.begin(); } const_iterator end() const { return _root.end(); } pair<iterator, bool> insert(const pair<const K, V>& kv) { return _root.Insert(kv); } /* operator[]:给一个key,返回这个key所对应的value的引用 简化前: mapped_type& operator[](const Key_type& k) { return (*(this->insert(make_pair(k,mapped_type())))).first).second; } 简化后 V& operator(const K& key) { pair<iterator,bool> ret=insert(key,V());//V():value的默认构造的匿名对象 return ret.first->second; } */ V& operator[](const K& k) { pair<iterator, bool> ret = insert(make_pair(k, V())); return ret.first->second; } private: //我们知道:map的Key不允许被修改,Value允许被修改 //因此pair里面的K加上const修饰 RBTree<K, pair<const K, V>, MapKeyofT> _root; }; }
七.改造后的红黑树代码
#pragma once namespace wzs { enum Color { RED, BLACK }; template<class T> struct RBTreeNode { RBTreeNode<T>* _pLeft; RBTreeNode<T>* _pRight; RBTreeNode<T>* _pParent; Color _color; T _data; //新插入的节点默认是红色 RBTreeNode(const T& data) :_pLeft(nullptr) , _pRight(nullptr) , _pParent(nullptr) , _color(RED) , _data(data) {} }; //给红黑树增加迭代器 template<class T, class Ref, class Ptr> struct __TreeIterator { typedef RBTreeNode<T> Node; typedef __TreeIterator<T, T&, T*> iterator; typedef __TreeIterator<T, Ref, Ptr> Self; Node* _node; __TreeIterator(Node* node) :_node(node) {} //const迭代器和普通迭代器的转化 __TreeIterator(const iterator& it) :_node(it._node) {} Ref operator*() { return _node->_data; } Ptr operator->() { return &_node->_data; } //找中序遍历的下一个节点 Self& operator++() { //1.左子树 根节点 右子树 //如果有右孩子,那就是右子树的最左节点 //如果没有右孩子,那往上找直到我是父亲的左孩子或者我的父亲是空节点为止,此时我的父亲就是下一个节点 Node* cur = _node; if (cur->_pRight) { Node* right = cur->_pRight; while (right->_pLeft) { right = right->_pLeft; } _node = right; } else { Node* parent = cur->_pParent; while (parent && parent->_pRight == cur) { cur = parent; parent = cur->_pParent; } _node = parent; } return *this; } bool operator!=(const Self& s) { return _node != s._node; } bool operator==(const Self& s) { return _node == s._node; } }; template<class K, class V,class KeyofT> class RBTree { typedef RBTreeNode<V> Node; public: typedef __TreeIterator<V, V&, V*> iterator; typedef __TreeIterator<V, const V&, const V*> const_iterator; iterator begin() { Node* cur = _pRoot; while (cur && cur->_pLeft) { cur = cur->_pLeft; } return iterator(cur); } iterator end() { return iterator(nullptr); } const_iterator begin() const { Node* cur = _pRoot; while (cur && cur->_pLeft) { cur = cur->_pLeft; } return const_iterator(cur); } const_iterator end() const { return const_iterator(nullptr); } //1,参数只需要传V即可(也就是红黑树的第二个模板参数) //对于set而言V就是Key //对于map而言,V就是pair<K,V> pair<iterator, bool> Insert(const V& data) { if (_pRoot == nullptr) { _pRoot = new Node(data); //根节点是黑色 _pRoot->_color = BLACK; return make_pair(iterator(_pRoot), true); } Node* cur = _pRoot, * parent = nullptr; while (cur) { //比我小,往左找 if (_kot(data) < _kot(cur->_data)) { parent = cur; cur = cur->_pLeft; } //比我大,往右找 else if (_kot(data) > _kot(cur->_data)) { parent = cur; cur = cur->_pRight; } //有重复元素,插入失败 else { return make_pair(iterator(cur), false); } } //找到空位置,开始插入 cur = new Node(data); Node* newnode = cur; //连接父亲和孩子 if (_kot(cur->_data) < _kot(parent->_data)) { parent->_pLeft = cur; } else { parent->_pRight = cur; } cur->_pParent = parent; //父亲是黑色,插入成功 if (parent->_color == BLACK) { return make_pair(iterator(newnode), true); } //父亲是红色,需要调整 //且此时爷爷一定是黑色 //根据叔叔的颜色来调整 while (parent && parent->_color == RED) { Node* grandParent = parent->_pParent; //爸爸是爷爷的左孩子 if (parent == grandParent->_pLeft) { Node* uncle = grandParent->_pRight; //根据叔叔的颜色来调整 //1.叔叔存在且为红 if (uncle && uncle->_color == RED) { //修改爸爸,叔叔,爷爷的颜色 uncle->_color = parent->_color = BLACK; grandParent->_color = RED; //此时视爷爷为新插入的红色节点继续向上影响 cur = grandParent; parent = cur->_pParent; } //2.叔叔不存在或者叔叔是黑 else { //1.我是爸爸的左孩子 if (parent->_pLeft == cur) { //对爷爷进行一次右旋 RotateR(grandParent); //把爷爷改成红色,爸爸改成黑色 grandParent->_color = RED; parent->_color = BLACK; //此时爸爸是黑色,无需break,当然break也可以,因此爸爸是黑色,下次循环就不会进入了 } //2.我是爸爸的右孩子 else { //1.对爸爸进行左旋 RotateL(parent); //2.对爷爷右旋 RotateR(grandParent); //3.孩子改成黑色,爷爷改成红色 cur->_color = BLACK; grandParent->_color = RED; //4.一定要break,如果不break的话,因为爸爸是红色,所以循环会继续,此时就乱套了 break; } } } //爸爸是爷爷的右孩子 else { Node* uncle = grandParent->_pLeft; //1.叔叔存在且为红 if (uncle && uncle->_color == RED) { uncle->_color = parent->_color = BLACK; grandParent->_color = RED; cur = grandParent; parent = cur->_pParent; } //2.叔叔不存在或者叔叔为黑 else { //1.我是爸爸的右孩子 if (parent->_pRight == cur) { //对爷爷左旋 RotateL(grandParent); //爷爷改成红色,爸爸改成黑色 grandParent->_color = RED; parent->_color = BLACK; //此时爸爸是黑色,无需break,当然break也可以,因此爸爸是黑色,下次循环就不会进入了 } //2.我是爸爸的左孩子 else { //1.对爸爸右旋 RotateR(parent); //2.对爷爷左旋 RotateL(grandParent); //3.把孩子改成黑色,爷爷改成红色 cur->_color = BLACK; grandParent->_color = RED; //4.一定要break,如果不break的话,因为爸爸是红色,所以循环会继续,此时就乱套了 break; } } } } _pRoot->_color = BLACK; return make_pair(iterator(newnode), true); } // 检测红黑树中是否存在关键字为key的节点,存在返回该节点的地址,否则返回nullptr Node* Find(const K& key) { Node* cur = _pRoot; while (cur) { if (cur->_data.first > key) { cur = cur->_pLeft; } else if (cur->_data.second < key) { cur = cur->_pRight; } else { return cur; } } return nullptr; } // 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测 bool IsValidRBTRee() { //1.空树是红黑树 if (_pRoot == nullptr) { return true; } //2.根节点不能为红色 if (_pRoot->_color == RED) { return false; } //3.为了验证性质: 红黑树的任意一条路径上的黑色节点个数相同 找参考值 size_t ReferenceCount = 0; Node* cur = _pRoot; while (cur) { if (cur->_color == BLACK) { ReferenceCount++; } cur = cur->_pLeft; } return _IsValidRBTRee(_pRoot, 0, ReferenceCount); } void InOrder() { _InOrder(_pRoot); } private: bool _IsValidRBTRee(Node* pRoot, size_t blackCount, size_t& ReferenceCount) { if (pRoot == nullptr) { if (blackCount != ReferenceCount) { cout << "存在黑色节点数量不相等的路径" << endl; return false; } return true; } //验证性质: 红黑树中不能存在连续的红色节点 //如果一个节点是红色,该节点一定不是根节点,因此该节点一定有父亲 //只需要保证红色节点的父亲不是红色节点即可 if (pRoot->_color == RED) { if (pRoot->_pParent->_color == RED) { cout << "存在连续的红色节点" << endl; return false; } } else { blackCount++; } return _IsValidRBTRee(pRoot->_pLeft, blackCount, ReferenceCount) && _IsValidRBTRee(pRoot->_pRight, blackCount, ReferenceCount); } // 右单旋 void RotateR(Node* pParent) { Node* subL = pParent->_pLeft; Node* subLR = subL->_pRight; Node* grandParent = pParent->_pParent; subL->_pRight = pParent; pParent->_pParent = subL; pParent->_pLeft = subLR; if (subLR) subLR->_pParent = pParent; if (grandParent == nullptr) { _pRoot = subL; _pRoot->_pParent = nullptr; } else { if (pParent == grandParent->_pLeft) { grandParent->_pLeft = subL; } else { grandParent->_pRight = subL; } subL->_pParent = grandParent; } } // 左单旋 void RotateL(Node* pParent) { Node* subR = pParent->_pRight; Node* subRL = subR->_pLeft; Node* grandParent = pParent->_pParent; pParent->_pParent = subR; subR->_pLeft = pParent; pParent->_pRight = subRL; if (subRL) subRL->_pParent = pParent; //说明此时pParent是_pRoot if (grandParent == nullptr) { _pRoot = subR; _pRoot->_pParent = nullptr; } //说明此时pParent所在的树是一颗子树,需要跟父亲链接 else { if (pParent == grandParent->_pLeft) { grandParent->_pLeft = subR; } else { grandParent->_pRight = subR; } subR->_pParent = grandParent; } } void _InOrder(Node* root) { if (root == nullptr) return; _InOrder(root->_pLeft); cout << root->_data.first << " " << root->_data.second << " "; _InOrder(root->_pRight); } private: Node* _pRoot = nullptr; KeyofT _kot; }; }
测试代码:
#include <iostream> using namespace std; #include <vector> #include "MySet.h" #include "MyMap.h" namespace wzs { void test1() { map<int, int> m; m.insert(make_pair(1, 1)); m.insert(make_pair(2, 1)); m.insert(make_pair(3, 1323)); m.insert(make_pair(4, 111)); m.insert(make_pair(3, 12)); m.insert(make_pair(1, 1)); map<int, int>::iterator it = m.begin(); while(it != m.end()) { it->second = 10; cout << it->first << " : " << it->second << endl; ++it; } cout << endl; map<int, int>::const_iterator cit = m.begin(); while (cit != m.end()) { //it->first = 10; //cit->second = 10; cout << cit->first << " : " << cit->second << endl; ++cit; } } void test2() { set<int> s; s.insert(1); s.insert(2); s.insert(3); s.insert(4); set<int>::iterator it = s.begin(); while (it != s.end()) { //*it += 1; cout << *it << " "; ++it; } cout << endl; set<int>::const_iterator cit = s.begin(); while (cit != s.end()) { //*cit += 1; cout << *cit << " "; ++cit; } cout << endl; } void test3() { //统计次数 map<string, int> countMap; vector<string> dict = { "hello","hi","who","hi","hi","who","which" }; for (auto& e : dict) { countMap[e]++; } for (auto& e : countMap) { cout << e.first << ":" << e.second << endl; } } } int main() { wzs::test1(); cout << endl; wzs::test2(); cout << endl; wzs::test3(); return 0; }
以上就是C++ 改造红黑树,封装map和set的全部内容,希望能对大家有所帮助!