c++ 第十一章:关联容器
2:在c++中,顺序容器和关联容器之间本质的区别在于:关键字,关联容器中的元素是按照关键字来保存的,顺序容器中的元素是按照它们在容器中的位置来保存的。
3:我们可以按照关键字是否有序
或者是否允许重复关键字
来区分关联容器。
- 关键字有序:map,set,multimap,multiset,它们按照有序存储的。
- 关键字无序:unordered_map,unordered_set,unordered_multimap,unordered_multiset。它们按照hash来组织。
4:关联容器不支持顺序容器相关的操作,例如push_front或者push_back,原因是关联容器中的值是按照关键字存储的。
5:带有multi
的都是允许重复关键字的。允许我们重复添加。
#include<iostream>
#include<vector>
#include<set>
using std::cout;
using std::cin;
using std::endl;
int main(int argc,char *argv[])
{
std::vector<int> ivec;
for(std::vector<int>::size_type i = 0;i != 10;++i) {
ivec.push_back(i);
ivec.push_back(i); //我们再次重复添加
}
//对于iset,不允许重复关键字
std::set<int> iset(ivec.cbegin(),ivec.cend());
//对于multiset允许重复关键字
std::multiset<int> miset(ivec.cbegin(),ivec.cend());
for(auto u:ivec) cout << u << " "; cout << endl;
for(auto u:iset) cout << u << " "; cout << endl;
for(auto u:miset) cout << u << " "; cout << endl;
return 0;
}
6:pair类型
一个pair保存两个数据成员,类似容器,pair是一个用来生成特定类型的模板,当创建一个pair时候,必须提供两个类型名,map中的元素类型就是pair。下面是pair的一些操作
操作 | 含义 |
---|---|
pair | 定义一个pair,类型为T1和T2,默认进行值初始化 |
pair p(v1,v2) = pairp = {v1,v2} | 用v1和v2分别对first和second成员进行了初始化 |
make_pair(v1,v2) | 返回一个用v1和v2初始化的pair,pair的类型从v1和v2推断 |
p.first | 返回第一个成员 |
p.second | 返回第二个成员 |
==,!=,<,>,<=,>= | pair支持算术运算符 |
7:关联容器迭代器
(1):map的value_type是pair类型的,第一个参数也就是.first即关键字,是不能被修改的。
(2):set的迭代器是const类型的。也就是我们只能读取一个set集合中的元素,而不能通过迭代器修改它。
(3):当我们使用迭代器遍历一个map、set、multimap、multiset时候,是按照关键字升序来遍历元素的。
(4):添加元素
操作 | 含义 |
---|---|
c.insert(v),c.emplace(v) | 插入v,对于map和set,当关键字不存在时才插入,返回的是一个pair(.first成员是一个迭代器,.second成员是一个bool值,表示插入是否成功) |
c.insert(b,e) | b和e是迭代器,表示一个范围 |
c.insert(p,v) | 类似于insert(v),但是将迭代器p作为一个提示,指出从哪里搜索新元素应该存储的位置 |
(5):删除元素
操作 | 含义 |
---|---|
c.erase(k) | 从c中删除每个关键字为k的元素,返回一个size_type的值,指出删除的数量 |
c.erase(p) | 从c中删除迭代器p指定的元素,返回一个迭代器指向被删除元素之后的元素 |
c.erase(b,e) | 删除迭代器b和e所表示范围中的元素。返回e |
(6):map的下标操作
- 只有map和unorderedmap支持下标操作。
- 如果下标操作接受的关键字不在map中,则map会自动创建一个关键字。
- map的下标操作返回的是一个mapped_type对象,也就是.second成员。
(7):访问元素
操作 | 含义 |
---|---|
c.find(k) | 返回一个迭代器,指向第一个关键字为k的元素,若k不在容器中,返回尾后迭代器 |
c.count(k) | 返回关键字等于k的数量 |
c.lower_bound(k) | 返回一个迭代器,指向第一个关键字不小于k的元素 |
c.upper_bound(k) | 返回一个迭代器,指向第一个关键字大于k的元素 |
c.equal_range(k) | 返回一个pair |
注意:
1:对于map和unordered_map类型,下标运算符提供了最简单的提取元素的方法,但是有时候我们不需要插入元素,只是想判断某个元素在不在容器中,这时候就应该使用find。
if(map1.find("foobar") == map1.end()) {
cout << "foobar is not in the map" << end;
}
2:对于multimap或者multiset,其中的元素可能具有相同的关键字,但是具有相同关键字的元素会在容器中连续存储。
//对于一个multimap的authors,一个作者可能对应多本书
std::string search_item("Tom");
auto entries = authors.count(search_item); //确定一共有多少个
auto iter = authors.find(search_item); //确定第一个位置
while(entries) {
cout << iter->second << endl;
++iter;
--entries; //直到entries为0,所有相邻的元素都会被访问
}
//实际上对于上面的问题,我们可以用lower_bound和upper_bound来处理,因为他们会返回一个范围
for(auto beg = authors.lower_bound(search_item),end = authors.upper_bound(search_item);beg != end;cout << *beg << endl)
//更简便的方法是使用equal_range:返回一个迭代器pair,第一个指向第一个与关键字匹配的类型,第二个指向最后一个,实际上也是返回了一个范围
for(auto pos = authors.equal_range(search_item);pos.first != pos.second;++pos.first) {
cout << pos.first->second << endl;
}
8:无序容器
使用hash函数
管理关键字。所有具有相同hash值的元素都会被保存在同一个桶中,如果容器允许重复关键字,则具有相同关键字的元素也会在一个桶中。无序容器的性能取决于hash函数的质量和桶的数量以及大小。下面是管理桶的函数
操作 | 含义 |
---|---|
桶接口 | |
c.bucket_count() | 正在使用的桶的数目 |
c.max_bucket_count() | 容器能容纳的最多的桶的数量 |
c.bucket_size(n) | 第n个桶中有多少个元素 |
c.bucket(k) | 关键字为k的元素在哪个桶 |
桶迭代 | |
c.begin(n),c.end(n) | 桶n的首元素迭代器和尾后迭代器 |
c.cbegin(n),c.cend(n) | 返回const_loacl_iterator |
哈希策略 | |
c.load_factor() | 每个桶平均元素数量,返回float |
c.max_load_factor() | c试图维护的平均桶的大小,返回float值,c会在需要时添加新的桶,使得load_factor <= max_load_factor |
c.rehash(n) | 重组存储,使bucket_count >= n |
c.reserve(n) | 重组存储,使得c可以保存n个元素且不必rehash |