2-STL(中)
关联式容器
关联式容器与序列式容器大不一样,此类容器在存储元素值的同时,还会为各元素额外再配备一个值(又称为“键”,其本质也是一个 C++ 基础数据类型或自定义类型的元素),它的功能是在使用关联式容器的过程中,如果已知目标元素的键的值,则直接通过该键就可以找到目标元素,而无需再通过遍历整个容器的方式。
此时关联式容器都是一个一个的键值对<key,value>,因此如果要查找或者排序的话,可以根据键值的大小入手。
种类
| 关联式容器名称 | 特点 |
|---|---|
| map | 定义在 |
| set | 定义在 |
| multimap | 定义在 |
| multiset | 定义在 |
#include<iostream>
#include<map>
#include<string>
int main(){
map<string,string> ma;
ma["sth"]="C";
ma["sb"]="project";
}
可以看出此时具有两个键对:<"sth","C">,<"sb","project">
pair
考虑到“键值对”并不是普通类型数据,C++标准库提供了 pair 类模板,其专门用来将 2 个普通元素 first 和 second(可以是 C++ 基本数据类型、结构体、类自定的类型)创建成一个新元素<first, second>。通过其构成的元素格式不难看出,使用 pair 类模板来创建“键值对”形式的元素。
pair();
pair(const type1 &a,const type2 &b);
template<class p,class q>pair<const pair<p,q> &pr>
有三种初始化方式,其中第三种为拷贝复制构造函数,借助另一个pair对象创建新的pair对象。
map
在使用 map 容器存储多个键值对时,该容器会自动根据各键值对的键的大小,按照既定的规则进行排序。默认情况下,map 容器选用std::less<T>排序规则(其中 T 表示键的数据类型),其会根据键的大小对所有键值对做升序排序。当然,根据实际情况的需要,我们可以手动指定 map 容器的排序规则,既可以选用STL标准库中提供的其它排序规则(比如std::greater<T>),也可以自定义排序规则。
使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改。map 容器中存储的各个键值对不仅键的值独一无二,键的类型也会用 const 修饰,这意味着只要键值对被存储到 map 容器中,其键的值将不能再做任何修改。
我们平时写算法题时,一般要使用using namespace std;命名全局空间,实际上我们在写项目或者其他东西时,如果定义全局空间的话会导致冲突,因此在平时是建议使用std::形式调用容器的。
对于map中的方法,主要使用以下:
| 成员方法 | 功能 |
|---|---|
| begin() | 返回指向容器中排好序后第一个键值对的双向迭代器。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
| end() | 返回指向容器排好序后最后一个元素所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 map 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。 |
| find(key) | 在 map 容器中查找键为 key 的键值对,如果成功找到,则返回指向该键值对的双向迭代器,也就是其位置;反之,则返回和 end() 方法一样的迭代器。 |
| erase() | 删除 map 容器指定位置、指定键(key)值或者指定区域内的键值对。 |
| emplace() | 在当前 map 容器中的指定位置处构造新键值对。其效果和插入键值对一样,但效率更高。 |
如何获取键对应的值?
1.map 类模板中对[ ]运算符进行了重载,这意味着,类似于借助数组下标可以直接访问数组中元素,通过指定的键,我们可以轻松获取 map 容器中该键对应的值。但是如果map中不存在该指定键的键值对,此时将不是访问而是添加一个键值对。对应的键位[]运算符中指定的键,其对应值取决于map中的数据类型
2.除了借助 [ ] 运算符获取 map 容器中指定键对应的值,还可以使用 at() 的成员方法。和前一种方法相比,at() 成员方法也需要根据指定的键,才能从容器中找到该键对应的值;不同之处在于,如果在当前容器中查找失败,该方法不会向容器中添加新的键值对,而是直接抛出 out_of_range 异常。
set
注意到使用 set 容器存储的各个键值对,要求键 key 和值 value 必须相等。
<'a','a'>,<'a','1'>注意到前者是可以正常初始化而后者不行。
map容器会自行根据键的大小对存储的键值对进行排序,set 容器也会如此,只不过 set 容器中各键值对的键 key 和值 value 是相等的,根据 key 排序,也就等价为根据 value 排序,同时使用 set 容器存储的各个元素的值必须各不相同。
std::set<std::string> sets;//初始化空set
std::set<std::string> sets{'内容'};
std::set<std::string> copy(sets);
这与当时构建的map是相同的,因此不再赘述。
其实考虑到set必须要两个键值是相同的,而map不需要,因此在通常时刻使用map;除非是那种对称存储的情况,set一般很少用到(我指algorithm中)。
置于multimap和multiset是可以存储多个键值的数据结构,有点类似于B树具有多个结点的树形结构,但是无论是map还是set,其中心思想皆为键值对来表示一个数据,相较于之前的vector它可以更高效的寻找一个值,这对我们在查找时有很大的帮助。
set方法的简单阐述
其实map的普适性是比set要高的,因为map包含了set的内容。
在此使用一个map未讲的插入方法:insert()
具体有以下四种实现方法:
1.只要给定目标元素的值,insert() 方法即可将该元素添加到 set 容器中,其语法格式如下:
普通引用方式传参
pair<iterator,bool> insert (const value_type& val);
右值引用方式传参
pair<iterator,bool> insert (value_type&& val);
可以看到有bool类型的pair参数,这是什么呢?是告诉你插入成功没有,因为set中不能包含重复的键值对,那么你现在可以知道如何删除重复元素了吧?
bool类型参数作用如下:
- 当向 set 容器添加元素成功时,该迭代器指向 set 容器新添加的元素,bool 类型的值为 true;
- 如果添加失败,即证明原 set 容器中已存有相同的元素,此时返回的迭代器就指向容器中相同的此元素,同时 bool 类型的值为 false。
2.insert() 还可以指定将新元素插入到 set 容器中的具体位置,其语法格式如下:
以普通引用的方式传递 val 值
iterator insert (const_iterator position, const value_type& val);
以右值引用的方式传递 val 值
iterator insert (const_iterator position, value_type&& val);
以上 2 种语法格式中,insert() 函数的返回值为迭代器(* it):
- 当向 set 容器添加元素成功时,该迭代器指向容器中新添加的元素;
- 当添加失败时,证明原 set 容器中已有相同的元素,该迭代器就指向 set 容器中相同的这个元素。
3.insert() 方法支持向当前 set 容器中插入其它 set 容器指定区域内的所有元素,只要这 2 个 set 容器存储的元素类型相同即可。
template <class InputIterator//迭代器>
void insert (InputIterator first, InputIterator last);
这个方法就有点像连接的作用了,当然这时候也要删除掉重复出现的键值对,不得不说set这个关联式容器有一个好的性质就是去重。
4.采用如下格式的 insert() 方法,可实现一次向 set 容器中添加多个元素:
void insert ( {E1, E2,...,En} );
其中,Ei 表示新添加的元素。

浙公网安备 33010602011771号