本期我们来学习map和set
目录
关联式容器
键值对
SGI-STL中关于键值对的定义:
pair
template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair(): first(T1()), second(T2())
{}
pair(const T1& a, const T2& b): first(a), second(b)
{}
};
树形结构的关联式容器
set
我们发现,set的模板参数比我们之前学的多了一个compare,这是仿函数,支持key比较大小的
我们来看它的构造,有拷贝构造,默认构造以及迭代器区间初始化
set的迭代器是双向迭代器
这里的key_type和value_type都是T,这里我们后面会讲
下面我们简单使用一下
使用没什么问题,我们在这里还发现一个问题,它的输出结果是有序的,因为它走的是中序遍历
而且还会去重,去重的原理是如果一个值已经有了,那就不插入
也可以使用范围for遍历
erase支持迭代器位置,值已经迭代器区间
我们根据情况选择即可
如果直接删除值,值不存在没什么问题,但如果用find先查找的话,这里就会报错,因为pos不存在
我们再看看这两个find有什么区别,一个是set自己的find,另一个是算法库里的
set自己的find最多查找高度次,时间复杂度是O(longN),而算法库的find是暴力查找,是O(N),所以我们最好不要用库里面的
还有一个count,这个set这里基本用不到
和find差不多,我们传一个元素,如果在返回1,不在就返回0
find是返回迭代器,而count是返回1或者0
还有这三个函数,是寻找边界的特殊情况用的,我们来看看例子就明白了
比如我们查找30和60,itlow得到的就是30,而itup是比60大的,因为迭代器区间是左闭右开,这样就可以和迭代器适配,可以保证30到60之间删除(包括60)
如果我们找35,得到的是40,lower_bound查找的是>=的值,upper_bound是>
equal_range也是一样,会查找一个左闭右开的区间
multiset
我们再看这个容器,它用起来和set是一样的
它和set的区别就是它允许有重复
如果我们要删除所有的5,我们上面的equal_range就是这样使用的 ,count也是在这里使用,这里可以算出有多少个5,我们可以认为这两个接口就是专门为multiset设计的,set有这两个接口只是为了保持接口一致罢了
当有重复值时,find返回的是中序的第一个
equal_range返回的是>=,如果给的值不存在,返回的就是不存在的区间
map
map这里也有三个type,下面我们简单使用一下
我们先看insert
map是kv模型,所以使用起来非常麻烦,那么可不可以像我们以前那样直接传呢?答案是可以的
原因是有make_pair
就像这样,这里就是map的插入
当然还可以更简洁一点,直接用{ }括起来,这是因为C++11支持多参数的构造函数隐式类型转换,也就是说这种写法在C++98是不能用的
接着我们来遍历,但是按照我们之前写的遍历,这里就出错了,原因是pair不支持流插入和流提取
那为什么这里不像我们写的那样直接用key和value,而要使用一个pair?
原因是operator*返回的话不方便,如果直接使用,C++是不支持返回两个值的,所以设计了一个pair结构
所以得这样写
如果觉得上面的写法麻烦还可以用->
大概就是这样
我们之前也说过,如果不是编译器优化,这里是要有两个箭头的 ,第一个是运算符重载,第二个是访问
也可以用范围for
first是不允许修改的,second可以修改
如果key相同,value不同,是不能插入了,key相同时不插入,不覆盖 ,也就是插入过程中只比较key,value无所谓
erase也是一样,只看key在不在
下面我们来看看[ ]
我们来看这个统计次数,我们以后就可以直接用map
而且代码可以优化,我们可以使用 [ ]
map的[ ] 不是常规的,而是给key,返回对应的value
后面的++我们懂,但是水果第一次出现是怎么回事呢?
它返回了这么个东西,借助了insert的返回值
我们可以看到insert的返回值是一个pair
大致翻译一下:这里返回一个pair,这个pair的first被设置为迭代器,要么指向新插入的元素,要么指向和key相等的元素,second被设计为true,如果key在里面返回false
也就是说,key已经在树里面,返回pair<树里面key所在节点的iterator,false>,如果key不在树里面,返回pair<新插入key所在节点的iterator,true>
如果我们自己设计就是这样,ret里除了key,另一个是匿名对象,根据value的变化而变化,如果key不在树里面, 那就插入成功,如果value是int那就是0,如果是string就是空的string,如果插入失败,也没有影响,因为insert只看key,而return时,first是迭代器,有了迭代器我们就可以取到second
我们结合起来理解一下,水果第一次出现时,insert,key是水果,value是int,int的匿名对象缺省值是0,然后返回这个次数,++一下就变成了1,如果有的话,那就插入失败,再返回次数,++次数就可以计数了
我们来一步一步看这个
我们经过dict["sort"]时是不影响的
这里是可以读的,也就是说,这里是查找+读
经过dict["map"]时,监视窗口多了一个map,value是空的,和我们前面看到的一样,[ ] 的本质是insert
接着我们修改了value(空的value)
这次我们修改了insert的value
最后一个就是插入+修改了(修改空的value)
[ ] 的功能非常多,我们用的时候也就要小心一点
multimap
这个也没啥好说的,对于map就和multiset对于set似的,就是可以有重复的key
不同的地方在于multimap没有提供operator[ ],所以insert也不一样了,不提供pair了,插入永远成功,其他功能一样
以上即为本期全部内容,希望大家可以有所收获
如有错误,还请指正