当前位置: 首页 > news >正文

C++笔记---set和map

1. 序列式容器与关联式容器

前面我们已经接触过STL中的部分容器如:string、vector、list、deque、array、forward_list等,这些容器统称为序列式容器,因为逻辑结构为线性序列的数据结构,两个位置存储的值之间一般没有紧密的关联关系,比如交换⼀下,他依旧是序列式容器。

顺序容器中的元素是按他们在容器中的存储位置来顺序保存和访问的。

关联式容器也是用来存储数据的,与序列式容器不同的是,关联式容器逻辑结构通常是非线性结构,两个位置有紧密的关联关系,交换一下,他的存储结构就被破坏了。

顺序容器中的元素是按关键字来保存和访问的。

关联式容器有map/set系列和unordered_map/unordered_set系列。

本章节讲解的map和set底层是红黑树,红黑树是一颗平衡二叉搜索树。

set/multiset是key搜索场景的结构, map/multimap是key/value搜索场景的结构。

set/map不支持相同的值插入,multiset/multimap支持相同的值插入。

2. pair类 

在正式介绍set和map之前,我们需要先了解一下pair类。

这个类也是一个类模板,顾名思义,它的作用就是存储两个数据,也即将两个数据绑定到一起。

在需要返回两个数据的场景下,我们就可以将两个数据存到pair中进行返回。

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){}// 支持隐式类型转换template<class U, class V>pair(const pair<U, V>& pr) : first(pr.first), second(pr.second){}
};// C++11之间常用的构造pair的函数
template <class T1, class T2>
inline pair<T1, T2> make_pair(T1 x, T2 y)
{return (pair<T1, T2>(x, y));
}

3. set的介绍

参考文档:set - C++ Reference,关于set的使用,将其当作key搜索场景的红黑树来使用即可。

set的原型:

template < class T,                        // set::key_type/value_typeclass Compare = less<T>,        // set::key_compare/value_compareclass Alloc = allocator<T>      // set::allocator_type> class set;

类型参数分别为:key的类型,比较器(通过仿函数实现,默认为小于比较,中序遍历得到升序序列),内存池。

一般来说,后两个参数有缺省值,的使用频率较低,我们在日常使用的过程中只需要传入第一个参数即可。

3.1 常用接口

STL的容器都是大同小异,这里只列出需要注意的。

3.1.1 构造函数
set();默认构造
template <class InputIterator>
set(InputIterator first, InputIterator last);
迭代器区间构造
set(const set& x);拷贝构造

在C++11之后还支持用列表构造:

// initializer list (5) initializer 列表构造
set (initializer_list<value_type> il,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());eg:
set<int> mySet1({1, 2, 3, 4});
set<int> mySet2 = {1, 2, 3, 4};

在原文档中,前两个构造函数的参数列表还包括比较器和内存池:

但是经过我自己的测试发现,如果在定义set的时候传入比较器或内存池,这个定义语句会被编译器识别成函数的声明:

也就是说,比较器和内存池依然只能在类型参数列表中传入。

 3.1.2 迭代器

这里就不列表了,set的迭代器是双向迭代器,函数还是那几个函数。

正向迭代器的是按照key值从小到大进行遍历。

除此之外,在set中无论是iterator还是const_iterator都不支持通过解引用修改set中的数据,因为这样会破坏红黑树的底层结构。

3.1.3 增删
(1)pair<iterator, bool> insert(const value_type& val);
(2)iterator insert(iterator position, const value_type& val);
(3)template <class InputIterator>
   void insert(InputIterator first, InputIterator last);

(1)按值插入

(2)指定位置插入

(3)将容器的迭代器区间内的值插入

(1)void erase(iterator position);
(2)size_type erase(const value_type& val);
(3)void erase(iterator first, iterator last);

(1)删除指定位置的值

(2)删除指定的值

(3)删除一个迭代器区间

insert函数(1)的返回值是一个pair:

插入成功时:iterator为新插入数据的迭代器,bool为true

插入失败时:iterator为已有的相同值的迭代器,bool为false

 insert函数(2)的第一个参数实质上只是一个插入建议,由于set的特殊的红黑树的底层结构,该插入建议可能不会被采纳。

 3.1.4 查找
iterator find (const value_type& val) const;查找某个值
size_type count (const value_type& val) const;查找某个值在set中有几个

set不支持相同的值进行插入,所以count的结果不是1就是0。

这样看来count似乎有点鸡肋,或者说与名称不符,但set中的count实际上是为了与multiset统一而存在的。

但count也并非一无是处,如果只想确定某个值存不存在的话,count就稍微比find方便一点:

if(mySet.find(24) != mySet.end())
{// ......
}if(mySet.count(24))
{// ......
}

3.2 multiset

基本与set相同,但是支持插入相同的数据,由此而引发的变化还有:

1. insert一定成功,插入函数(1)的返回值不再是pair而只有iterator。

2. count返回数据在set中的个数。

3. 如果有多个相同的值,无论是删除还是查找,都是对中序遍历的第一个进行操作。

 4. map的介绍

参考文档:map - C++ Reference,关于map的使用,将其当作key/value搜索场景的红黑树来使用即可。

map的原型:

template < class Key,                                     // map::key_typeclass T,                                       // map::mapped_typeclass Compare = less<Key>,                     // map::key_compareclass Alloc = allocator<pair<const Key,T> >    // map::allocator_type> class map;

类型参数分别为:key的类型,value的类型,比较器(通过仿函数实现,默认为小于比较,中序遍历得到升序序列),内存池。

除此之外,pair在map中起到了将key和value绑定到一起的作用,所以在map的结点中存的数据实际上是pair,而没有单独存储key和value。

typedef pair<const Key, T> value_type;

同样,大多数情况下只需要传入前两个参数即可。

4.1 常用接口

同样,此处只列出较为重要或与其他容器有所不同的。

4.1.1 构造函数
map();默认构造
template <class InputIterator>
map (InputIterator first, InputIterator last);
迭代器区间构造
map (const map& x);拷贝构造

在C++11之后支持列表构造:

// initializer list (5) initializer 列表构造
map (initializer_list<value_type> il,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());eg:
map<string, int> myMap1({{"张三", 18}, {"李四", 21}, {"王五", 35}});
map<string, int> myMap2 = {{"张三", 18}, {"李四", 21}, {"王五", 35}};

内层大括号会通过pair的列表构造隐式类型转换为pair<string, int>类型。 

关于参考文档中给出的函数原型,依然存在着和set相同的问题。

 4.1.2 迭代器

函数还是那些函数,迭代器还是双向迭代器。

但与set不同的是,map的迭代器支持对value进行修改,而依然不支持对key进行修改。

由于map结点中存的是pair<key, value>,所以按照逻辑来说,map的迭代器解引用得到的就是pair<key, value>。

在pair中,key对应的是first,value对应的是second:

map<string, int> myMap(...);
map<string, int>::iterator it = myMap.begin();
while(it != myMap.end())
{cout << "key:" << (*it).first << " value:" << *(it).second << endl;cout << "key:" << it->first << " value:" << it->second << endl;
}
4.1.3 增删
(1)pair<iterator, bool> insert(const value_type& val);
(2)iterator insert(iterator position, const value_type& val);
(3)template <class InputIterator>
   void insert(InputIterator first, InputIterator last);

(1)按值插入

(2)指定位置插入

(3)将容器的迭代器区间内的值插入

(1)void erase(iterator position);
(2)size_type erase(const key_type& k);
(3)void erase(iterator first, iterator last);

(1)删除指定位置的值

(2)删除指定的值

(3)删除一个迭代器区间

函数原型与set完全一样,除了erase函数(2),只需要key即可找到对应的数据进行删除。

但是要切记,这里的value_type是pair。

insert函数(1)的返回值逻辑也与set相同,但是只要key相同就不支持继续插入了。

4.1.4 查找
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;
按键值key查找
size_type count (const key_type& k) const;查找某个键值key在set中有几个

 同样这里的count函数的返回值也只能是1或0。

4.1.5 operator[]

没错,map重载了"[]"。

但是,按理来说,只有支持随机迭代器的容器才可以重载"[]",而map不是双向迭代器吗?

看了函数原型你就明白了:

mapped_type& operator[] (const key_type& k);

所以,这个"[]"的功能是根据key来返回value的引用,而与迭代器没有关系。

但这个函数并不简单:

当key存在时,返回其对应的value的引用。

当key不存在时,在map中插入key,并给value默认值,然后返回value的引用。

所以map中的"[]"可以说是"查找,修改,插入"三位一体。

这是由于operator[]的内部实现是基于insert函数完成的:

// operator[]的内部实现
mapped_type& operator[] (const key_type& k)
{// 1、如果k不在map中,insert会插⼊k和mapped_type默认值,同时[]返回结点中存储//mapped_type值的引⽤,那么我们可以通过引⽤修改返映射值。所以[]具备了插⼊+修改功能// 2、如果k在map中,insert会插⼊失败,但是insert返回pair对象的first是指向key结点的//迭代器,返回值同时[]返回结点中存储mapped_type值的引⽤,所以[]具备了查找+修改的功能pair<iterator, bool> ret = insert({ k, mapped_type() });iterator it = ret.first;return it->second;
}

 4.2 multimap

基本与map相同,但是支持插入相同的数据,由此而引发的变化还有:

1. insert一定成功,插入函数(1)的返回值不再是pair而只有iterator。

2. count返回数据在map中的个数。

3. 如果有多个相同的值,无论是删除还是查找,都是对中序遍历的第一个进行操作。

4. 不再支持operator[],因为同一个key存在多组值。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • ElasticSearch数据类型和分词器
  • (十五)、把自己的镜像推送到 DockerHub
  • python中网络爬虫框架
  • 机械快门,电子快门,电子前帘快门 的原理
  • SPECFEM手册的一些翻译(Chapter 4)
  • Qt 状态机编程,双层状态机,实现暂停恢复
  • 【手写数据库内核组件】1001词法分析器,语言被程序识别的第一步,将语句分解为最小词根token
  • 常见框架漏洞复现
  • 不同语言的switch/case语句
  • 【通讯协议】S32K142芯片——LIN通信的学习和配置
  • ActiveMQ 的消息持久化策略
  • K8s Calico替换为Cilium,以及安装Cilium过程
  • 解决Vue 3中Element Plus el-color-picker 组件消失的问题
  • 828华为云征文|华为云Flexus X实例docker部署最新Appsmith社区版,搭建自己的低代码平台
  • 浅显易懂的Git教程
  • Android Volley源码解析
  • CSS 专业技巧
  • github从入门到放弃(1)
  • Java Agent 学习笔记
  • JavaScript的使用你知道几种?(上)
  • PhantomJS 安装
  • Quartz初级教程
  • 从伪并行的 Python 多线程说起
  • 基于webpack 的 vue 多页架构
  • 利用阿里云 OSS 搭建私有 Docker 仓库
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 以太坊客户端Geth命令参数详解
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • 7行Python代码的人脸识别
  • # Apache SeaTunnel 究竟是什么?
  • $(selector).each()和$.each()的区别
  • (4.10~4.16)
  • (BAT向)Java岗常问高频面试汇总:MyBatis 微服务 Spring 分布式 MySQL等(1)
  • (C语言)逆序输出字符串
  • (iPhone/iPad开发)在UIWebView中自定义菜单栏
  • (二)基于wpr_simulation 的Ros机器人运动控制,gazebo仿真
  • (二)原生js案例之数码时钟计时
  • (附源码)spring boot基于小程序酒店疫情系统 毕业设计 091931
  • (简单) HDU 2612 Find a way,BFS。
  • (牛客腾讯思维编程题)编码编码分组打印下标(java 版本+ C版本)
  • (图)IntelliTrace Tools 跟踪云端程序
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (转)Linux NTP配置详解 (Network Time Protocol)
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • .bat批处理(七):PC端从手机内复制文件到本地
  • .config、Kconfig、***_defconfig之间的关系和工作原理
  • .net core 6 集成 elasticsearch 并 使用分词器
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • .NET连接MongoDB数据库实例教程
  • @param注解什么意思_9000字,通俗易懂的讲解下Java注解
  • [ Linux ] Linux信号概述 信号的产生
  • []T 还是 []*T, 这是一个问题
  • []指针
  • [Android] Binder 里的 Service 和 Interface 分别是什么