set&map高频出现在笔试题&竞赛中,做总结如下
本文通过 http://www.cplusplus.com/reference 的 set&map API 介绍 set&map
有关红黑树的更多知识,请通过「任意门」跳转到我的另一篇博文(TODO)
引言
set&map 基于红黑树构成,借助其近似平衡树的特性,查询结构中元素的算法复杂度仅为 logN
以 AVL树 为例,其完全平衡树结构使得 $1073741824(2^{30})$ 个数字中查找一个数字最多只需要 30 次。
因此,基于红黑树封装的 set&map 广泛运用于比赛与实际项目中。
set/map 概述
- 主要特点
- set/ map 天然具备去重复+排序功能
- set/ map 查询效率非常高(近似平衡二叉树)
- 结构区别
- map 基于红黑树(K-V结构)实现,封装了插入、迭代器等接口
- set 基于红黑树实现,但可以看作抛弃了 value ,只存储 key
- 实际源码通过
rbtree<key, key>
实现set、rbtree<key, pair<key, value>>
实现map
此外对于
- set/multiset 区别
- multiset在底层红黑树中,key可以有重复值。
- 在接口方面几乎没有区别
set
set 插入
1. 直接插值
1 | //原型 |
其中:
value_type
: The first template parameter (T)(模版参数)- 返回值中
pair::first
指向插入的元素的迭代器 - 当该元素是新插入的值时(插入前set没有这个值),返回值中
pair::second
中的布尔值为true
2. 通过迭代器插值
1 | //原型 |
其中:
- 新的元素将会插入在原来迭代器的位置
- 插入并不是强制插入(不是一定插入在选择的迭代器的位置),而是遵循给定的排序
- 意味着插入从指定迭代器的位置开始寻找合适的插入点
- 在已知一个合适的迭代器时,可以有利于性能提升
3. 插入两个迭代器中所有数据(泛型)
1 | //原型 |
set 其他接口
其他常用接口比较简单,不在赘述。函数原型如下
1 | //删除 可选参数:迭代器、原始数据类型、迭代器范围 |
map
map 插入
map的插入总体与set没有太大区别,在此仅强调不同部分
1 | std::map<char,int> mymap; |
其中对于map.find()
1 | //原型 |
在这里map的接口相比set有一个较大的区别是:
- value_type: pair
- mapped_type : The second template parameter (T)(第二个模版参数 value)
上述可以看作:value_type:pair<key, value>
map 其他接口
对于 count
、upper_bound
、erase
方法,除了参数明确为key_type(模版参数)外,与set没有区别。
map 对方括号的重载
map中有一个十分实用的使用方法:通过重载方括号访问和插入元素
1 | //原型 |
见以上代码,我们可以通过重载方括号实现:
- 直接插入一个元素,格式为
mymap[key] = value;
- 给定 key 输出对应 value 格式为
mymap[key]
根据 www.cplusplus.com 给出的源码参考,该重载函数的定义为
1 | mapped_type& operator[] (const key_type& k) |
简单剖析上述代码:
首先调用
1 | //适当增减空格以增加可读性 |
此时map中一定存在希望插入的一对元素的值,插入动作已经完全完成
1 | ( this->insert(make_pair(k,mapped_type())) ).first /*insert返回pair<iterator,bool> |
拿到了该对元素的迭代器
1 | *((this->insert (make_pair(k,mapped_type()) )).first) |
将上一步拿到的迭代器解引用,得到含有该对元素的pair类型
1 | (*((this->insert (make_pair(k,mapped_type()) )).first)).second; |
取得pair.second,即该对元素的value值
对于以下用法
1 | cout << mymap['a'] << endl; |
目的是获取 ‘a’ 对应的 value
实际先做插入操作,尽管插入失败,但仍能得到对应的元素对应的迭代器,继而取得目标 value
以上,重载方括号的实现极大地增强了可读性,同时提升了开发效率
不仅是 C++,读者可以在各类编程语言都能看到这种格式的操作,可见其魅力