罗湖网站建设费用,怎么取消网站备案,自己怎么开发app,网站分析内容文章目录引入key-value模型map和set底层setset的几个重要接口mapmap几个重要的接口map和set的封装引入
对于map和set的引入#xff0c;我们用一道在程序中常见的问题解决#xff1a; 给定一个数组int arr[]{1,2,1,3,1,4,1,5,5,2,3,4,5};#xff0c;给出以下问题的解决方案我们用一道在程序中常见的问题解决 给定一个数组int arr[]{1,2,1,3,1,4,1,5,5,2,3,4,5};给出以下问题的解决方案
找出出现次数最多的元素去除数组中重复的元素并输出任意顺序
这些问题在没有学习map和set之前并没有很好的解决方法但是map和set并不是唯一的解决方法第二个问题之前我们解决的方法一般就开辟一个新数组tmp遍历arr中所有元素对于arr中任意元素遍历tmp数组如果元素在tmp中未出现就插入。第一个问题只需将数组类型改成一个结构体该结构体包含一个元素和他出现的次数则在最后一步如果arr中元素已经出现在tmp中就改变tmp中该元素对应结构体的次数。
我们发现arr在tmp中插入时每一次都要遍历一遍tmp数组是否能提升查找效率呢
所以我们现在需要一个容器
该容器中不存在重复元素该容器中查找一个元素效率要高
key-value模型
key-value模型适用于上面的问题1每一个元素key和他出现的次数value一 一对应但是每次查找tmp数组都是以key为依据查找。 举个例子一个英汉字典就是典型的key-value模型英文key是你搜索的依据中文value是查找的内容两者一一对应
map和set底层
有一个数据结构完美符合上面我们需要容器的两个条件——红黑树。首先红黑树不能插入重复元素所以可以做到去重的效果。又因为满足二叉搜索树所以查找效率比高。而且比二叉搜索树稳定。所以map和set的底层采用的是红黑树
set
set存储元素的类型为key容器中将该类型定义为value_type
set的几个重要接口 insert pairiterator,bool insert (const value_type val);pairiterator,bool insert (value_type val);注意insert的返回值是一个pair类型first是插入元素的迭代器如果val在set中不存在返回插入新元素的迭代器如果在set中存在返回set中值为val的那个元素的迭代器second代表是否插入成功 operator 由于set底层采用的是红黑树迭代器则采用的是树的中序遍历所以遍历的结果应该是一个有序数组 map
map中存储的元素的类型是pairkey,value,容器中将该类型定义为value_type
map几个重要的接口 insertpairiterator,bool insert (const value_type val);
template class P
pairiterator,bool insert (P val);返回值 注意insert的返回值是一个pair类型first是插入元素的迭代器如果val在set中不存在返回插入新元素的迭代器如果在set中存在返回set中值为val的那个元素的迭代器second代表是否插入成功 finditerator find (const key_type k);
const_iterator find (const key_type k) const;返回值 如果找到了返回该元素的迭代器找不到返回map::end() operator[]mapped_type operator[] (const key_type k);
mapped_type operator[] (key_type k);这里mapped_type是我们map中存储元素类型pairkey,value中的value 这个运算符重载给我们map的使用带来了巨大的方便map[key]和(map.insert(key,value()).first)-second是等价的。 operator 由于map底层采用的是红黑树迭代器则采用的是树的中序遍历所以遍历的结果应该是一个有序数组 map和set的封装
我们对于map和set封装始终是在红黑树的基础上进行封装的红黑树的代码可以参考blog红黑树 红黑树的代码接下来的封装都是在这个代码的基础上进行修改的 封装的结构为
这里以map的封装为例 map的封装弄明白了set就非常简单了 首先需要将红黑树的节点模板改一下 这里修改后使得树节点里面存储的是ValueType类型ValueType类型实际上就是修改前pairK,V我们可以发现ValueType实际上就是我们在外层插入时插入时传进去的类型
template class ValueTypestruct TreeNode{}template class K, class ValueType, class GetKey
class RBTree{};那么问题来了
已经传入了ValueType为什么要传入K 传入Key类型只是作为返回值类型因为find函数参数为const K x仅此而已。 红黑树在插入搜索时需要比较key值节点里面存储的是ValueType如何比较 有些同学会认为很简单直接(ValueType)x.second不就行了这样确实可以但是没有考虑一个问题set也需要使用这份红黑树代码作为底层如果用上面的代码set中的ValueType和K是同一个类型这时就会出现错误。 我们的解决方法是使用仿函数Get_key来取出ValueType中的Value让上层来决定如何取出方法。这就是仿函数的妙处
剩下来只需要将红黑树中函数封装到上层map中就可以了 #include RedBlackTree.h
#include utilitynamespace sht
{template class Key, class Valueclass map{struct Get_Key_From_ValueType{Key operator()(const std::pairKey, Value v){return v.first;}};public:typedef std::pairconst Key, Value ValueType; //注意这里定义的ValueType是实际树节点里面存的内容typedef typename sht::RBTreeKey, ValueType, Get_Key_From_ValueType::iterator iterator;typedef typename sht::RBTreeKey, ValueType, Get_Key_From_ValueType::const_iterator const_iterator;std::pairiterator,bool insert(const std::pairKey, Value x){return tree.insert(x);}Value operator [](const Key x){auto ret tree.insert(std::make_pair(x, Value()));return (ret.first)-second;}const_iterator begin() const // 常量迭代器{return tree.begin();}const_iterator end() const{return tree.end();}iterator begin() // 普通迭代器 注意普通迭代器的key也是无法修改的{return tree.begin();}iterator end(){return tree.end();}private:sht::RBTreeKey, ValueType, Get_Key_From_ValueType tree;};
}这里map还有一个设计的技巧如果是const对象那么begin和end要返回const迭代器这里重载了begin和end函数参考这篇blog。这个设计很巧妙
接下来是一个硬骨头迭代器的设计 template class T, class ptr, class ref //ref是class RBTreeiterator{public:typedef TreeNodeT Node;typedef RBTreeiteratorT, ptr, ref Self;RBTreeiterator(Node *xnullptr){p x;}//迭代器的运算符重载Self operator();Self operator--();bool operator(const RBTreeiterator x)bool operator!(const RBTreeiterator x)T operator*()ptr operator-() Node *p;};然而一个问题出现了非const对象想要使用const迭代器时就会出现问题 例如mapint,int::const_iterator itx.begin();这时就会报错因为是非const对象调用的begin是非const成员函数返回的是普通迭代器所以报错的原因是缺乏普通迭代器到const迭代器的转化 重写一个拷贝构造函数就完美解决了在迭代器里面的iterator巧妙的构造出了普通迭代器类型很巧妙的解决了该问题
迭代器的运算符重载中还有一个难点是operator ——即按中序取当前节点的下一个节点这种问题参考LeetCode剑指 Offer II 055. 二叉搜索树迭代器我这里就不赘述了。