【C++进阶】map和set的使用
🥕个人主页:开敲🍉
🔥所属专栏:C++🥭
🌼文章目录🌼
1. 序列式容器和关联式容器
2. set系列的使用
2.1 set 和 multiset
2.2 set 类的介绍
2.3 set 的构造和迭代器
2.4 set 的增删查
2.5 multiset 和 set 的区别
2.6 两道练习题
3. map 系列的使用
3.1 map 和 multimap
3.2 map 类的介绍
3.3 pair 类型的介绍
3.4 map 的构造和使用
3.5 map 的增删查
3.6 map 的数据修改
3.7 构造遍历及增删查的用法参考
3.8 multimap 和 map 的区别
3.9 两道练习题
1. 序列式容器和关联式容器
前面我们已经接触过STL中的部分容器如:string、vector、list、deque、array、forward_list等,这些容器统称为序列式容器,因为逻辑结构为线性的数据结构,两个相邻位置存储的值一般没有紧密的关联关系,比如交换或者修改,不会破坏它的结构。顺序容器中的元素是按它们的存储位置来顺序存储和访问的。
关联式容器也是用来存储数据的,与序列式容器不同的是,关联式容器的逻辑结构通常是非线性结构的,两个位置有紧密的关联关系,比如将相邻两个位置交换,它的结构就被破坏了。关联式容器有 map/set 系列和 unordered_maxp/unordered_set 系列。
本篇讲解的 map 和 set 的底层是 红黑树,也就是平衡二叉搜索树。set是 key 搜索场景的结构,map是 key/value (键和值)搜索场景的结构。
2. set系列的使用
2.1 set 和 multiset
参考文档:<set> - C++ Reference (cplusplus.com)
2.2 set 类的介绍
① set的声明如下:
T就是 set 底层关键字的类型。
② set 默认要求 T 支持大小的比较,如果不支持或者想要按照自己的需求比较,可以实现仿函数传给第二个模板参数。
③ set 底层的存储数据的内存是从空间配置器中申请的,如果需要可以自己实现内存池,传给第三个参数。
④ 一般情况下,我们不需要传后两个模板参数,用它的缺省参数即可。
⑤ set 的底层是红黑树,增删查的效率是 O(logN),迭代器遍历时走的是二叉搜索树的中虚遍历,因此遍历结果是有序的,默认为升序。
⑥ 前面部分我们已经学习了 vector / list 等容器的使用,STL容器的接口设计高度相似,因此不需要再一个个的去看是什么功能,这里重点讲几个比较重要的:
insert:往 set 中插入元素,插入的方式和二叉搜索树一致,插入值比当前节点小走左边,比当前节点大走右边。
支持指定值插入、迭代器插入。
用法:
不支持插入相同的值:
erase:删除 set 中的节点,删除方式和二叉搜索树一致:如果是叶子节点直接删除,非叶子节点分为两种情况:① 有一个孩子,将孩子托付给父亲,删除节点 ② 有两个孩子,去到右子树中找最小值所在节点,将要删除节点的值和最小值节点的值交换,随后删除最小值节点。
支持指定元素删除、迭代器删除。
用法:
lower_bound:指定一个值,返回 set 中第一个 ≥ 该值的迭代器
用法:
upper_bound:指定一个值,返回 set 中第一个 > 该值的迭代器
find:寻找 set 中指定元素。
用法:
count:返回 set 中指定元素的个数,但是由于 set 不允许重复元素插入,因此这里返回的只有 0 和 1 ,但是这里还是返回个数是因为要和后面的 multiset 保持一致。
用法:
2.3 set 的构造和迭代器
set 的构造我们关注以下几个接口即可:
① 无参默认构造:
② 迭代器区间构造:
③ 拷贝构造:
④ initializer list:initializer 列表构造:
set 支持正向和反向迭代遍历,遍历默认按升序排序,因为底层是二叉搜索树,遍历时按照中序遍历;支持迭代器就意味着支持范围 for,在上面接口的用法中我也用到了范围 for。set 的迭代器不支持迭代器修改数据,如果修改关键字数据,会破坏 set 的结构。
2.4 set 的增删查
set 的增删查我们关注以下接口即可:
增:insert
① 单个数据插入。如果数据已经存在于 set 中,则插入失败
② 列表插入。同样的,set 中已经存在的值不会插入
③ 迭代器区间插入。重复的值不会插入
删:erase
① 删除迭代器位置的值
② 删除指定元素。如果不存在则删除失败,返回00;反之,返回1
③ 删除一段迭代器区间的值
查:find、count、lower_bound、upper_bound
① 查找某一个值,返回该值所在的迭代器;如果找不到则返回 end()迭代器
② 查找某一个值,返回该值的个数;如果找不到,则该值的个数为0
③ 返回第一个大于等于 指定元素 位置的迭代器
④ 返回第一个大于 指定元素 位置的点带起
2.5 multiset 和 set 的区别
multiset 和 set 的使用完全类似,主要区别在于 multiset 支持重复数据的存储,那么因此 insert、erase、find、count 都根据这种特性进行了改变,这里直接看用法的区别:
① insert:
可以看到,这里可以插入相同的值。
② erase:
可以看到,当我们删除 4 时,会将 ms 中所有的 4 都删掉。
③ find:
查找时,如果有多个相同的元素比如 7,则会返回 中序遍历 的第一个 7 。
④ count:
返回 multiset 中该元素的个数。
2.6 两道练习题
349. 两个数组的交集 - 力扣(LeetCode)
LCR 022. 环形链表 II - 力扣(LeetCode)
不论你之前使用什么方法做的,如果没有用 set 来做过,强烈建议你试试,完全就是降维打击。
3. map 系列的使用
3.1 map 和 multimap
参考文档:<map> - C++ Reference (cplusplus.com)
3.2 map 类的介绍
map 的声明如下,key 就是关键字,T 是 map底层 value 的类型,set 默认要求 key 支持大小比较,如果不支持或者想要按照自己的需要进行比较,则需要自行实现仿函数传给第三个模板参数,map 底层存储数据的内存是从空间配置器申请的。一般情况下,我们不需要传后两个模板参数。map 的底层是红黑树,增删查改的效率是 O(logN),迭代器遍历走的是二叉搜索树的中序遍历,因此遍历结果是有序的。
3.3 pair 类型的介绍
map 底层的红黑树节点中的数据,使用 pair<key,T> 来存储键值对数据。下面是库中的 pair,可以参考:
3.4 map 的构造和使用
map 的构造和使用我们关注以下几个接口即可(用法和 set 完全类似,这里就不演示用法了):
① 无参数默认构造:
② 迭代器区间构造:
③ 拷贝构造:
④ 列表构造:
insert:与 set 一样,都是按照二叉搜索树的结构插入数据。区别在于,map 支持重复数据的存储。
erase:指定元素删除。跟 set 的区别在于,如果有多个重复的指定元素,map 会全部删除。
find:查找指定元素。和 set 一样,返回指定元素所在的迭代器。
count:查找指定元素的个数。和 set 一样,返回指定元素的个数。
lower_bound:返回第一个 ≥ 指定元素的迭代器。
upper_bound:返回第一个 > 指定元素的迭代器。
operator[]:因为 map 支持修改,因此重载了 [] 符号,使得能够修改 map 中的值value,但是不能修改 key
map 支持正向和反向迭代器遍历,遍历默认按照键 key 升序顺序,因为底层是二叉搜索树,遍历走的是中序遍历;支持迭代器就意味着支持范围 for,map 支持修改 value,不支持修改 key,因为修改关键字会破坏结构。
3.5 map 的增删查
map 的增删查关注以下几个接口:
增:
① 指定元素插入:
② 迭代器位置插入:
③ 迭代器区间插入:
④ 列表插入:
删:
① 迭代器位置删除:
② 指定元素删除:
③ 迭代器区间删除:
查找:find、count、lower_bound、upper_bound
① 查找指定元素,返回指定元素所在迭代器:
② 查找指定元素,并返回指定元素的个数:
③ 查找第一个 ≥ 指定元素的迭代器并返回:
④ 查找第一个 > 指定元素的迭代器并返回:
3.6 map 的数据修改
前面提高了 map 支持修改 value,而不支持修改 key,那么 map 如何修改 value 呢?
第一个方法就是通过迭代器修改,使用迭代器遍历时或者使用 find 查找返回时,修改迭代器所在key 的 value。
第二个方法上面也有提及,就是 operator[],map 重载了 [],就是方便我们进行修改操作。当然,operator[],不仅仅可以进行修改操作,还可以进行插入和查找数据的操作,因此它是一个多功能接口。
用法:
3.7 构造遍历及增删查的用法参考
① 构造+遍历:
② insert:
③ erase:erase 与 set 完全类似,都是根据键 key 去删除节点,map 无非就是多删除了一个 value,这里就不演示了。
3.8 multimap 和 map 的区别
multimap 和 map 的使用完全类似,主要区别点在于 multimap 支持关键值 key 重复,因此 insert、erase、count、find 都会因为这个特性与 map 有所差异。这里跟 set 和 multiset 的区别是一样的,比如 find 的元素有多个,则返回中序遍历的第一个。最后就是 multimap 不支持[],也就是没有重载 operator[],因为支持重复的键 key,因此只能插入而不能修改。
3.9 两道练习题
138. 随机链表的复制 - 力扣(LeetCode)
692. 前K个高频单词 - 力扣(LeetCode)
创作不易,点个赞呗,蟹蟹啦~