目录
前言
set 是 K模型的容器,map 是 KV模型的容器,但是它们的底层实现都是红黑树实现,即用红黑树可以封装出 set和 map,之前的篇章已经讲过红黑树了,这里就不解释了。
接下来对红黑树进行改造,用改造后的红黑树封装出 set 和 map
一、改造红黑树
使用的代码是之前篇章红黑树实现的代码,改造后红黑树的框架如下:
enumColour{ RED, BLACK, }; template<classT>structRBTreeNode{ //构造函数RBTreeNode(constT&data) :_data(data) ,_left(nullptr) ,_right(nullptr) ,_parent(nullptr) ,_col(RED) {} //成员变量T_data; RBTreeNode<T>*_left; RBTreeNode<T>*_right; RBTreeNode<T>*_parent; Colour_col; }; template<classT, classRef, classPtr>struct__RBTreeIterator{ typedefRBTreeNode<T>Node; typedef__RBTreeIterator<T, Ref, Ptr>Self; typedef__RBTreeIterator<T, T&, T*>iterator; //构造__RBTreeIterator(Node*node) :_node(node) {} // 普通迭代器的时候,他是拷贝构造// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器__RBTreeIterator(constiterator&s) :_node(s._node) {} Refoperator*() { return_node->_data; } Ptroperator->() { return&_node->_data; } booloperator!=(constSelf&s)const { return_node!=s._node; } booloperator==(constSelf&s)const { return_node==s._node; } Self&operator++() { if (_node->_right) { Node*min=_node->_right; while (min->_left) { min=min->_left; } _node=min; } else { Node*cur=_node; Node*parent=cur->_parent; while (parent&&cur==parent->_right) { cur=cur->_parent; parent=parent->_parent; } _node=parent; } return*this; } Self&operator--() { if (_node->_left) { Node*max=_node->_left; while (max->_right) { max=max->_right; } _node=max; } else { Node*cur=_node; Node*parent=cur->_parent; while (parent&&cur==parent->_left) { cur=cur->_parent; parent=parent->_parent; } _node=parent; } return*this; } //成员变量Node*_node; }; template<classK, classT, classKeyOfT>classRBTree{ typedefRBTreeNode<T>Node; public: typedef__RBTreeIterator<T, T&, T*>iterator; typedef__RBTreeIterator<T, constT&, constT*>const_iterator; iteratorbegin() { Node*left=_root; while (left->_left) { left=left->_left; } returniterator(left);// 使用匿名对象构造返回 } const_iteratorbegin()const { Node*left=_root; while (left->_left) { left=left->_left; } returnconst_iterator(left); } iteratorend() { returniterator(nullptr); } const_iteratorend()const { returnconst_iterator(nullptr); } //插入pair<iterator, bool>Insert(constT&data) {} //中序遍历voidInOrder() { _InOrder(_root); } //检查红黑树特性boolIsBalance() {} private: //检查每条路径的黑色节点是否相等 && 是否出现连续红色节点boolCheck(Node*root, intblackNum, intref) {} void_InOrder(Node*root) {} //左单旋voidRotateL(Node*parent) {} //右单旋voidRotateR(Node*parent) {} private: Node*_root=nullptr;//缺省值};
代码过多就不贴完了,完整代码链接:
https://gitee.com/Maple_fylqh/code/blob/master/code_c++/Set_and_Map/Set_and_Map/RBTree.h
改造红黑树就是给红黑树增加迭代器相关的,这棵泛型的红黑树可以同时封装出set 和 map
1.1 红黑树迭代器相关
迭代器模板参数在 list 模拟实现已经有过解释,这里不解释了,模板参数 T 是红黑树存放数据的类型,下面解释
template<class T, class Ref, class Ptr>
上面迭代器类只需提供构造函数即可, 拷贝构造和赋值不需要自己实现,默认生成的即可,当前迭代器赋值给另一个迭代器是不需要深拷贝的,浅拷贝就可以,拷贝构造也是;析构释放空间的事情不归迭代器类管理,迭代器类只需构建迭代器对象即可
上面红黑树迭代器实现的接口有:
//构造__RBTreeIterator(Node*node) :_node(node) {} // 普通迭代器的时候,他是拷贝构造// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器__RBTreeIterator(constiterator&s) :_node(s._node) {} //解引用,获取数据Refoperator*(){} //返回数据的地址Ptroperator->(){} //比较两个迭代器是否不相等booloperator!=(constSelf&s)const{} //比较两个迭代器是否相等booloperator==(constSelf&s)const{} //前置++Self&operator++(){} //前置--Self&operator--(){}
说明一下,迭代器都是支持普通迭代器构造const迭代器
实现如下,这里它利用了构造函数和拷贝构造函数的特性,当传入的迭代器是普通迭代器时:参数类型与拷贝构造参数要求一致,它就是拷贝构造;当传入的的迭代器是 const迭代器,参数类型与拷贝构造函数不匹配,类型有差异,它就变成了构造函数
typedef__RBTreeIterator<T, T&, T*>iterator; // 普通迭代器的时候,他是拷贝构造// const迭代器的时候,他是构造,支持用普通迭代器构造const迭代器__RBTreeIterator(constiterator&s) :_node(s._node) {}
前置++ 和前置-- 是红黑树迭代器实现的重点
前置++
一个结点的正向迭代器进行 ++ 操作后,应该根据红黑树中序遍历的序列找到当前结点的下一个结点
如下图,传入第一个节点是1,迭代器++ 下一个节点是6,再次++ 节点为8,迭代器要达到这种效果才可以满足需求
思路:
- 传入一个节点,如果节点右不为空就先往右边走,找到右边节点最小的节点,该节点就是 ++ 的下一个节点
- 传入一个节点,如果节点的右子树为空,去找祖先,迭代依次往上走,找到孩子是父亲左的那个祖先节点
代码如下:
Self&operator++() { if (_node->_right)//传入节点右不为空,找右边最小节点 { Node*min=_node->_right; while (min->_left) { min=min->_left; } _node=min; } else)//传入节点为空 { Node*cur=_node; Node*parent=cur->_parent; while (parent&&cur==parent->_right) { cur=cur->_parent; parent=parent->_parent; } _node=parent; } return*this; }
前置--
一个结点的迭代器进行 -- 操作后,就是红黑树中序遍历找到的结点的前一个结点
如下图,比如传入第一个节点是27,迭代器-- 下一个节点是25,再次 -- 节点为22,迭代器要达到这种效果才可以满足需求
--的思路和 ++ 的思路类似,只不过是反过来找
思路:
- 传入一个节点,如果节点左不为空就先往左边走,找到左边节点最大的节点,该节点就是 -- 的下一个节点
- 传入一个节点,如果节点的左子树为空,去找祖先,迭代依次往上走,找到孩子是父亲右的那个祖先节点
代码如下:
Self&operator--() { if (_node->_left)//传入节点左不为空就先往左边走, { Node*max=_node->_left; while (max->_right) { max=max->_right; } _node=max; } else//传入节点左为空 { Node*cur=_node; Node*parent=cur->_parent; while (parent&&cur==parent->_left) { cur=cur->_parent; parent=parent->_parent; } _node=parent; } return*this; }
其他就不介绍了
1.2 红黑树接口相关
template<classK, classT, classKeyOfT>classRBTree{ typedefRBTreeNode<T>Node; public: typedef__RBTreeIterator<T, T&, T*>iterator; typedef__RBTreeIterator<T, constT&, constT*>const_iterator; private: Node*_root=nullptr;//缺省值};
说明一下模板参数,为了实现泛型RBTree:
- 红黑树第一个参数是 K(Key),主要用于查找数据;
- 红黑树第二个模板参数 T ,可以识别是 map 还是 set,因为 set和 map传给 T的参数不一样,set 传的是 Key,map 传的是键值对 pair;
- 红黑树第三个参数是 KeyOfT,这是一个仿函数,由于红黑树中 T 存的可能是K,也可能是pair。那我们插入的时候该怎么比较结点的大小呢,对于set是没有问题的,直接可以用T比较,但是map就不行了,我们要取出pair的 first来进行比较
二、set代码
namespacefy{ template<classK>classset { structSetKeyOfT { constK&operator()(constK&key) { returnkey; } }; public: typedeftypenameRBTree<K, K, SetKeyOfT>::const_iteratoriterator; typedeftypenameRBTree<K, K, SetKeyOfT>::const_iteratorconst_iterator; iteratorbegin()const { return_t.begin(); } iteratorend()const { return_t.end(); } pair<iterator, bool>insert(constK&key) { pair<typenameRBTree<K, K, SetKeyOfT>::iterator, bool>ret=_t.Insert(key); returnpair<iterator, bool>(ret.first, ret.second); } private: RBTree<K, K, SetKeyOfT>_t; }; voidTestSet() { intarr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 }; set<int>s; for (auto&e : arr) { s.insert(e); } set<int>::iteratorit=s.begin(); while (it!=s.end()) { cout<<*it<<" "; ++it; } cout<<endl; for (auto&e : s) { cout<<e<<" "; } cout<<endl; } }
三、map代码
namespacefy{ template<classK, classV>classmap { structMapKeyOfT { constK&operator()(constpair<constK, V>&kv) { returnkv.first; } }; public: typedeftypenameRBTree<constK, pair<constK, V>, MapKeyOfT>::iteratoriterator; typedeftypenameRBTree<constK, pair<constK, V>, MapKeyOfT>::const_iteratorconst_iterator; iteratorbegin() { return_t.begin(); } iteratorend() { return_t.end(); } const_iteratorbegin()const { return_t.begin(); } const_iteratorend()const { return_t.end(); } pair<iterator, bool>insert(constpair<constK, V>&kv) { return_t.Insert(kv); } V&operator[](constK&key) { pair<iterator, bool>ret=insert(make_pair(key, V())); returnret.first->second; } private: RBTree<K, pair<constK, V>, MapKeyOfT>_t; }; voidTestMap() { intarr[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 }; map<int, int>m; for (auto&e : arr) { m.insert(make_pair(e, e)); } map<int, int>::iteratorit=m.begin(); while(it!=m.end()) { it->second++; cout<<it->first<<"->"<<it->second<<endl; ++it; } map<string, int>countMap; stringstr[] = { "苹果", "西瓜", "香蕉", "草莓", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" }; for (auto&e : str) { countMap[e]++; } for (auto&kv : countMap) { cout<<kv.first<<":"<<kv.second<<endl; } } }
这篇文章写起来逻辑有点不通(难写)dog,主要是用来复习的
----------------我是分割线---------------
文章到这里就结束了,下一篇即将更新