通过红黑树的学习(C++之红黑树-CSDN博客)让我了解到map和set的底层如何实现,这一次我们来对map和set进行封装。
目录
1.map和set底层原理
2.map和set的定义
3.map和set的仿函数
4.map和set的插入
5.map和set的迭代器
5.1迭代器的构造
5.2解引用
5.3成员访问操作符
5.4 == 与!=
5.5begin()
5.6end()
5.7++
5.8--
6.map总体代码
7.set总体代码
1.map和set底层原理
我们都知道map和set的底层都是红黑树,但是map是kv模型,set是k模型,对于红黑树来说这两种不同的模型 可以通过控制模版参数来区分。
map简化后的源码
template<class Key,class T,class Compare = less<Key>,class Alloc=alloc> class map { public:typedef Key ket_type;typedef T data_type;typedef pair<const Key, T> value_type; private:typedef rb_tree < key_type, value_type,selectlst<value_type>, key_compare, Alloc> rep_type; };
set简化后的源码:
template<class Key, class Compare = less<Key>, class Alloc = alloc> class set { public:typedef Key ket_type;typedef Key data_type;typedef Compare key_type;typedef Compare value_compare; private:typedef rb_tree < key_type, value_type,identity<value_type>, key_compare, Alloc> rep_type; };
那么红黑树是怎么通过模板来区分map和set 的呢?
rb_tree 的 value 决定 是map的key/value 还是 set的key:
如果是set容器,那么它传入底层红黑树的模板参数就是Key和Key:
template<class K>class set{private:RBTree<K,K> _t;};
如果是map容器,传入底层红黑树的模板参数就是Key和Key和value的键值对:
class map{private:RBTree<K, pair<const K,V>> _t;};
通过上面,我们可以知道,对于set和map的区别:我们只要通过第二个模板参数就能进行区分那是不是第一个模板参数就没有意义了呢?
对于insert(const Value&v)来说,需要放入存入的值,确实是这个样子的,插入的值是value,对于set就是key,对于map就是pair。
但是对于find(const Key&key)来说,查找的参数不是value,找的不是pair而是Key,对于map容器来说就不行了。
2.map和set的定义
set:
template<class K> class set { private: RBTree<K,K> _t; };
map:
class map { private: RBTree<K, pair<const K,V>> _t; }
3.map和set的仿函数
我们将传入的data直接进行比较
Q:
对于set是Key,可以比较
对于map是pair,我们无法直接进行比较,我们的期望是使用pair键值对中的first去比较,与second无关。
由于底层的红黑树不知道传的是map还是set容器,当需要进行两个结点键值的比较时,底层红黑树传入的仿函数来获取键值Key,进行两个结点键值的比较:这个时候我们就需要仿函数了,如果是set那就是用于返回T当中的键值Key,如果是map那就是用于返回pair的first:
仿函数/函数对象也是类,是一个类对象。仿函数要重载operator()
map:
namespace bt {template<class K, class V>class map{struct MapkeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}}; }
set:
namespace bt {template <class K>class set // 仿函数,重载operator(){struct SetKeyOfT{const K& operator()(const K& key){return key;}}; }
我们的查找函数如下:
KeyOfT kot;//仿函数对象
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{if (kot(cur->_data)<kot(data)){parent = cur;cur = cur->_right;}else if (kot(cur->_data)>kot(data)){parent = cur;cur = cur->_left;}else{return false;}
}
4.map和set的插入
直接cv大法,从红黑树那里拿到了代码
//map
bool insert(const pair<K, V>& kv)
{return _t.Insert(kv);
}//set
bool insert(const K& key)
{return _t.Insert(key);
}
5.map和set的迭代器
5.1迭代器的构造
template<class T,class Ref,class Ptr>
struct __RBTreeIterator
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T,Ref,Ptr> Self;typedef __RBTreeIterator<T, T&, T*> iterator;Node* _node;__RBTreeIterator(Node*node):_node(node){}//普通迭代器的时候,它是拷贝构造//const迭代器的时候,它是构造,支持用普通迭代器构造const迭代器__RBTreeIterator(const iterator& s):_node(s._node){}
}
5.2解引用
Ref operator*(){return _node->_data;}
5.3成员访问操作符
Ptr operator->(){return &_node->_data;}
5.4 == 与!=
bool operator !=(const Self & s) const{return _node != s._node;}bool operator ==(const Self& s) const{return _node == s._node;}
5.5begin()
begin():返回中序(左、根、右)第一个结点的正向迭代器,即最左节点,返回的是最左节点,直接找最左节点即可
template<class K, class T,class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef __RBTreeIterator<T> iterator;iterator begin(){Node* left = _root;while (left && left->_left){left = left->_left;}return iterator(left);}
}
5.6end()
end():返回中序(左、根、右)最后一个结点下一个位置的正向迭代器,这里用空指针
template<class K, class T,class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef __RBTreeIterator<T> iterator;iterator begin(){Node* left = _root;while (left && left->_left){left = left->_left;}return iterator(left);}iterator end(){return iterator(nullptr);}
}
5.7++
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;}
一个结点的正向迭代器进行++
操作后,根据红黑树中序(左、根、右)找到当前结点的下一个结点,中序的第一个节点是最左,迭代器的++
怎么去找:
- 如果节点的右子树不为空,
++
就是找右子树的最左节点- 如果节点的右子树为空,
++
就是找祖先(孩子是父亲的左的那个祖先
5.8--
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;}
如果是根,–就是左子树,找到左子树最大的那一个(最右节点)
- 如果节点的左子树不为空,
--
找左子树最右的节点- 如果节点的左子树为空,
--
找祖先(孩子是父亲的右的祖先)
6.map总体代码
#include"RBTree.h"namespace bt
{template<class K, class V>class map{struct MapkeyOfT {const K& operator()(const pair<const K, V>& kv){return kv.first;}};public://typename:没有实例化的模板,区分不了是静态变量还是类型,typename告诉编译器是类型typedef typename RBTree<K, pair<const K, V>, MapkeyOfT>::iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapkeyOfT>::const_iteratorconst_iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}const_iterator begin() const{return _t.begin();}const_iterator end() const{return _t.end();}pair<iterator, bool> insert(const pair<const K, V>& kv){return _t.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}private:RBTree<K, pair<const K, V>, MapkeyOfT> _t;};}
7.set总体代码
#include"RBTree.h"namespace bt
{template <class K>class set // 仿函数,重载operator(){struct SetKeyOfT{const K& operator()(const K& key){return key;}};public://typename:没有实例化的模板,区分不了是静态变量还是类型,typename告诉编译器是类型typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;//key不可以修改typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;iterator begin() const{return _t.begin();}iterator end() const{return _t.end();}pair<iterator, bool> insert(const K& key){pair<typename RBTree<K, K, SetKeyOfT>::iterator, bool> ret = _t.Insert(key);return pair<iterator, bool>(ret.first, ret.second);//用普通迭代器构造const迭代器}private:RBTree<K, K, SetKeyOfT> _t;};}