C++:set和map的介绍

9 篇文章 0 订阅
2 篇文章 0 订阅

目录

关联式容器

键值对

set介绍: 

set的模板参数列表 

 set的双向迭代器:

insert的使用和set的特性:

 set的删除:

set的find: 

 lower_bound  、 upper_bound:

 multiset:

map介绍: 

pair< const Key , T> 

 和搜索二叉树的KV模型对比:

插入操作 

遍历操作 

注意事项:

使用范围for进行遍历:

 写法1:

写法2:

构造操作: 

operator[] 

operator[]的使用 

 operator[]的多种写法

查找操作

使用find进行查找

使用operator[]进行查找

count 

使用second排序操作 和 multimap

 

关联式容器

关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是结构的 键值对,在数据检索时比序列式容器效率更高。

键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代 表键值,value表示与key对应的信息。

set介绍: 

1. set是按照一定次序存储元素的容器

2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。 set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。

3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行 排序。

4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对 子集进行直接迭代。  

5. set在底层是用二叉搜索树(红黑树)实现的。 

6.与map/multimap不同,map/multimap中存储的是真正的键值对,set中只放 value,但在底层实际存放的是由构成的键值对。

7. set中插入元素时,只需要插入value即可,不需要构造键值对。

8. set中的元素不可以重复(因此可以使用set进行去重)。

9. 使用set的迭代器遍历set中的元素,可以得到有序序列

10. set中的元素默认按照小于来比较

11. set中查找某个元素,时间复杂度为:$log_2 n$

12. set中的元素不允许修改(为什么?) 

set的模板参数列表 

  • T: set中存放元素的类型,实际在底层存储的键值对。
  • Compare:set中元素默认按照小于来比较,同时是一个仿函数
  • Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理  

 set的双向迭代器:

insert的使用和set的特性:

可以看出,当我们遍历set的迭代器时,遍历set内部的内容可以发现,set内部存储的形式是一个搜索二叉树,当遍历set时遍历出的结果是一个搜索二叉树中序遍历的结果,且set和搜索二叉树一样,具有去重功能和具有不允许修改内部数值的功能,因为修改内部的数据内容会导致set内部的存储结构失效。

 set的删除:

set的find: 

find是找到后返回该节点的迭代器

 lower_bound  、 upper_bound:

lower是找大于等于后面要找的值,而upper是要找大于要找的数值。

在这里因为lower找的是大于等于后面数值,然后返回那个数值节点的 迭代器,upper也是如此,这里upper是第一个比60大的数值的节点的迭代器,然后使用erase进行删除把者之间的数值进行删除,[30,70)不删除70

 multiset:

multiset其实是set的一种分支,和set相比,它不具备set的去重功能,但是内部的底层和set一样是一个搜索二叉树,但这个二叉树是允许重复的元素出现。 

这里如果按照搜索二叉树是不能有等于的,且大于在右小于在左边,这里就是左右都可以随便插入,当然相等的数值也不一定是连续的,因为底层还是树,所以需要寻找空位进行插入

但是如果要是find寻找重复的数值,那么multiset的find找的是中序排序后的第一个,也就是:查找X,查找到X的节点后,那么这个节点的左子树没有X节点,那么他就是中序的第一个X节点,如果左子树有X这个节点,那么接着往左子树方向继续查找X节点。 

map介绍: 

1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元 素。

2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的 内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型 value_type绑定在一起,为其取别名称为pair: typedef pair value_type;

3. 在内部,map中的元素总是按照键值key进行比较排序的。

4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序 对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。

5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。

6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。  

  • key: 键值对中key的类型
  • T: 键值对中value的类型
  • Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比 较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户 自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
  • Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的 空间配置器
  • 注意:在使用map时,需要包含头文件。 

pair< const Key , T> 

pair<const Key , T> 是一个内部类函数也是一个类模板

 和搜索二叉树的KV模型对比:

就是把搜索二叉树的K和V存入了一个类模板结构中 ,而pair这个类模板的内部中的 first表示键 Key,second表示值 Value

插入操作 

对于map的inser操作t在有了pair后,实际上就是插入一个pair 

dict.insert({"String","字符串"})是一种隐式类型转化,但是需要注意这种隐式类型转化要和构造函数中的initializer_list<value_type>进行区分:

如上图代码所示,上图代码是一个构造函数。

遍历操作 

可以看到图上代码中的<<出现了问题,这其实和pair有关,因为pair内部其实是两个数值,所以迭代器并不知道应该返回谁,且pair并不支持流插入操作,所以需要进行深入的操

前文说过,pair内部的Key 和 Value数值是使用first和second来替代的。

 简化写法1:

简化写法2:可以省略箭头,和list的operator->的箭头省略操作一样

 可以看出如果我们要实现map底层代码中的的迭代器,那么将会较为的困难。

注意事项:

在遍历map的时候,pair的first是不允许进行修改的,而second却可以进行修改:

firs是key相对于树的节点和各类节点之间具有关系,所以是不能进行修改的,因为map是靠key进行排序的!

使用范围for进行遍历:
 写法1:

写法2:

写法2是建立在当前编译器的C++版本处在C++17,而在此之前的写法是C++11版本的

构造操作: 

operator[] 

map的operator[]和之前的operator[]不同,以前[]内部是一个下标,就例如vector的operator[] []内部的参数是下标,而map的operator[]的[]内部是一个key,内部是一个key然后返回一个key对应的value

如下图所示, operator[]的内部和insert有关,主要是利用了map的insert的性质:

  • 如果插入的元素在map中已经存在了,那么就返回一个pair<存在的key所在的节点的迭代器,false>
  • 如果插入的元素在map不存在,则返回一个pair<新插入的key所在的迭代器,true>

所以插入是否成功关键在于返回的pair内部的second,且无论插入是否成功key所在节点的迭代器都会被返回,而如果插入的是同一个key但是value不一样,那么就算插入了也是无效,因为编译器不会更改value的数据。

operator[]的使用 

  • 从这里看出insert是一个查找的功能
  • 所以为什么[]内部是放置key的就是为了传递key让后让insert去查找这个key是否在map中是否存在,如果存在则返回false,而后通过pair获取key所在的节点的迭代器(ret.frist)然后再通过这个迭代器来获取这个迭代器所对应的second也就是value
  • 图中的it->second是一个简写
 operator[]的多种写法

注意插入+修改 和 修改,如果有[]内部的key数值,那么就变成修改,没有就变成插入功能 

查找操作
使用find进行查找

这个函数表达的是 水果的名字表示为frist 也就是 Key ,而水果出现的次数则是second 也就是Value。

使用find查找string arr[]内部的所有数据是否又在map中出现过,如果有,find会返回一个该数据在map中的节点位置,以迭代器的形式进行返回,如果没有会返回一个end, 那么当返回值不等于end时则会让map中的这个数据的value次数加1。

而代码会在,没有出现这个数据是,进行插入操作,同时插入这个数据也要把这个数据出现的次数1也需要进行插入。

使用operator[]进行查找

  • 在这里的countMap[e]++内部的e是一个key表示arr[]内部的数据,如果arr内部的数据没有在countMap中出现,那么则会进行插入+修改操作
  • 例如苹果没有出现则会插入苹果,而苹果出现的次数在插入的过程中会被初始化成0,而在++后变成了1
  • 而·如果苹果存在与countMap中后[]会进行查找并且返回,而又因为countMap的scond内部存储的是次数,而[]无论查找成功还是失败都会返回,因为这里查找成功后返回了second然后因为++scond则开始+1
  • 所以苹果次数会变成2
count 

给一个key然后返回这个key在map内部的个数,可以进行判断key是否存在:

如上代码所示,输入一个str字符串,看是否在map中是否存在,如果存在则输出在,不存在则输出不在。

使用second排序操作 和 multimap

 使用另一个map,将次数和字符串进行颠倒,使用遍历赋值的方法,如上图所示,原先的string变成了second的位置,而原先的int变成了frist的位置

  • 进行对比发现,少了一个草莓,原因是草莓和香蕉的frist重复了,且香蕉是后面进行遍历赋值的,而且使用了[]导致了后面变成了key=2的value=草莓被修改成了key=2 value=香蕉
  • 因为使用了[]然后查找到了之后被修改了,这是因为[]具有修改的操作
  • 而如果使用了insert则会导致香蕉插入失败,因为insert是不具有修改功能的,所以因为key都等于2,所以当轮到香蕉时,算是找到了所以无法进行修改

 所以想要进行second排序则需要使用 multimap,这是因为multimap允许冗余,也就是允许map的key重复,而map不允许key重复

 这个是查找多个重复的key的 ,最后返回的是第一个重复的key的迭代器,和最后一个重复key的迭代器的前一个位置  的函数


 

  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值