我的C++primer长征之路:关联容器

关联容器

概述

关联容器支持普通容器的操作,不支持顺序容器中位置相关的操作,如push_back等,也不支持构造函数或插入操作的接收一个元素值和数量值得操作,如vector< int>v(10,0)。

普通容器的操作
类型别名
iterator 此容器类型的迭代器
const_iterator 可以读取元素,但不能修改元素的迭代器类型
size_type 无符号整数类型,足够保存此种容器类型最大可能容器的大小
diference_type 带符号整数类型,足够保存两个迭代器之间的距离
value_type 元素类型
refernce 左值引用;与value_type&含义相同
const_refernce 元素的const左值类型(const value_type&)
构造函数
C c; 默认构造函数,构造空容器(array除外)
C c1(c2) 用c2来拷贝构造c1
C c3(b, e) 用迭代器b,e范围内的元素拷贝构造c3(array不支持)
C c{a,b,d,…} 列表初始化c
赋值与交换
c1 = c2 将c1中的值替换成c2的值
c1 = {a,b,d,…} 将c1中的值替换成列表元素
a.swap(b) 交换a和b的元素
swap(a,b) 与上等价
大小
c.size() c中元素的数目(forward_list不支持)
c.max_size() c中可保存的最大元素数目
c.empty() 若c中存储了元素,则返回false,否则返回true
添加删除元素(array不支持,不同容器接口不尽相同)
c.insert(args) 将args插入c
c.emplace(inits) 使用inits构造c中的一个元素
c.erase(args) 删除args指定的元素
c.clear() 删除c中所有元素,返回void
关系运算符
==, != 所有容器都支持相等(不等)运算符
<,<=,>,>= 关系运算符(无序关联容器不支持)
获取迭代器
c.begin(),c.end() 返回指向c中首元素和尾元素之后位置的迭代器
c.cbegin(),c.cend() 返回常迭代器
反向容器的额外成员
reverse_iterator 逆序寻址元素的迭代器
const_reverse_iterator 不修改元素的逆序迭代器
c.rbegin(),c.rend() 返回指向c的尾元素和首元素之前位置的迭代器
c.crbegin(),c.crend() 返回const_reverse_iterator

关联容器的迭代器都是双向的。

关联容器额外的类型别名
key_type 此容器类型的关键字类型
mapped_type 每个关键字关联的类型,只适用于map
value_type 对于set,与key_value相同,对于map,为pair<const key_type, mapped_type>

关联容器通常不使用泛型算法,也就是STL中的算法。因为set中元素是const的,map中pair.first也是const的,会修改容器元素的算法都不能使用。可以用于只读取元素的算法,但是会比较慢,不如关联容器内置的算法。

有序容器

pair

存放在头文件utility中。

pair操作
pair<T1, T2>p; 定义类型为T1、T2类型的pair,并进行值初始化。
pair<T1, T2>p(v1,v2); 如上,其first和second成员被初始化为v1,v2。
pair<T1,T2>p = {v1,v2}; 等价于上面。
make_pair(v1,v2); 返回v1,v2初始化的pair,其类型从v1,v2类型推断出来。
p.first 返回p的名为first的共有数据成员。
p.second 返回p的名为second的共有数据成员。
p1 relop p2 关系运算符(<,<=,>,>=)按字典序定义:当p1.first<p2.first && p1.second < p2.second成立时,p1 < p2返回true。
p1 == p2 first和second两个成员分别相等时成立。

map(multimap)的value_type是一个pair,其first成员保存const的关键字,second成员保存值。

遍历map

map<string, int>word_count = {{"Tim", 20}, {"Jack", 15}, {"Rose", 30}};
auto map_iter = word_count.cbegin();
while(map_iter != word_count.cend()){
    cout << map_iter->first << endl;
    cout << map_iter->second << endl;
    ++map_iter;
}

map、multimap、set、multiset的迭代器按照关键字升序遍历元素。

添加元素:

map
map.insert(v); v是value_type对象
map.emplace(args); 返回pair,其first是一个迭代器,指向具有指定关键字的元素,second是指示插入是否成功的bool值,对于multimap和multiset总会插入给定元素,并返回指向新元素的迭代器。
map.insert(b, e); b,e是迭代器,表示一个c::value_type类型的范围。
map.insert({…}) 插入花括号的值列表,返回void。对于set和map,只会插入不在容器中的关键字,对于multiset和multimap则每个元素都会插入。
map.insert(p,v) p是迭代器,表示从p开始搜索新元素应该存储的位置,返回一个指向具有给定关键字的迭代器。
map.emplace(p,v) 同上版本

对于插入的返回值,可以写成这样:

pair<map<string, int>::iterator, bool> ret = word_count.insert(make_pair(word, 1));
//插入的返回值是一个pair,其first是word_count同类型的迭代器,,second是表示插入是否成功的bool值。

set的迭代器是const的,也就是说迭代器只能访问set成员,而不能修改。

向multimap、multiset中插入元素:

multimap<string, string>authors;
//插入 第一个元素,关键字为Tom
authors.insert({"Tom", "Book1"});
//插入第二个元素,关键字还是Tom
authors.insert({"Tom", "Book2"});//正确,multi关联容器关键字不唯一。

删除元素

关联容器定义了3个版本的erase:

关联容器删除元素
c.erase(k) 删除c中每个关键字为k的元素。返回size_type值,表示删除元素的数量。
c.erase§ 从c中删除迭代器p指向的c中的元素,且不能是c.end()。返回指向p之后元素的迭代器。
c.erase(b, e) 删除迭代器b和e所表示范围内的元素,返回迭代器e。

map的下标操作(查找元素时推荐用find函数):

map和unordered_map支持下表运算符和对应的at()函数。set类、multi类不支持。

map\unordered_map的下标操作和at()函数
c[k] 返回关键字为k的元素,若c中不存在k,则添加一个关键字为k的元素并进行值初始化。
c.at(k) 访问关键字为k的元素,带参数检查,若k不在c中,抛出out_of_range异常

下标操作要注意的是,如果下标关键字不在map中,会为该容器自动创建一个该关键字的元素,且其映射值会进行值初始化!

map<string, int>word_count; //这是空的map
word_count["Tom"] = 1; //map中没有关键字为Tom的元素,自动创建该元素。。

并且由于下标操作会改变容器,所以只能对非const的map和unordered_map进行下标操作!

map下标运算符返回值与其他下标运算符返回值另一个不同之处:同常情况下,解引用一个迭代器返回的类型和下标运算返回的类型是一样的,但是对map进行下标操作是,返回的是mapped_type对象,解引用map的迭代器时会得到value_type对象。
map的下标运算符与其他下标运算符一样,返回一个左值,我们可以对下标操作的元素进行读和写。

cout<< word_count["Tom"]; //提取关键字为Tom的元素,打印结果是1.
++ word_count["Tom"]; //修改元素
cout<< word_count["Tom"] //会输出2

访问元素

访问关联容器中元素的操作
下标操作和at()只适用于非const的map和unordered_map。
c.find(k) 返回指向第一个关键字为k的迭代器,若k不在容器中,则返回尾后迭代器。
c.count(k) 返回关键字等于k的元素的数量。对于不允许重复关键字的容器,返回值只能是0或1.
c.lower_bound(k) 返回指向第一个关键字不小于k的元素的迭代器。
c.upper_bound(k) 返回指向第一个关键字大于k的元素的迭代器。
c.equal_range(k) 返回一个迭代器pair,表示关键字等于k的元素的范围,若k不在c中,则pair的两个成员均为c.end()。

如果只需要确定元素是否在一个容器中,使用find(k)而不是[k]!

对于multimap和multiset,如果具有多个相同的关键字,则这些元素会相邻存储。

对于c.lower_bound()和c.upper_bound(),两者组合可得到关键字等于k的元素的范围。

for (auto begin = word_count.lower_bound(k), end = word_count.upper_bound(k); begin != end; ++begin){
    cout << begin.second <<endl; //输出关键字等于k的元素的值
}

如果lower_bound和upper_bound返回相同迭代器,说明该关键字不在容器中。

equal_range(k)
直接返回关键字等于k的元素的范围,等价于lower_bound和upper_bound。其返回值是一个迭代器pair。第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配的元素的后一个位置

无序容器

unordered_map、unordered_set、unordered_multimap、unordered_multiset。
无序关联容器不是使用比较运算符(<,<=,>,>=,默认使用<来进行比较)来组织元素的,而是使用哈希函数和关键字类型的==运算符。
无序容器在存储上组织为一个桶,使用哈希函数将元素映射到相应桶。对哈希函数的要求是,相同的参数,哈希函数必须总是产生相同都得结果,但是不同参数产生相同的结果也是允许的。

与有序容器不同,不能直接定义关键字类型为自定义类型的无序容器,如果确实需要定义这样类型的容器,需要提供该自定义类型的hash模板。

无序容器管理操作
桶接口
c.bucket_count() 正在使用的桶的数目。
c.max_bucket_count() 容器能容纳最多的桶的数量。
c.bucket_size(n) 第n个桶有多少个元素。
c.bucket(k) 关键字为k的元素在哪个桶中。
桶迭代
local_iterator 可以用来访问桶中元素的迭代器类型。
const_local_iterator 桶迭代器的const版本。
c.begin(n),c.end(n) 桶n的首元素迭代器和尾后迭代器。
c.cbegin(n),c.cend(n) 上面的const版本。
哈希策略
c.load_factor() 每个桶的平均元素数量,float型。
c.max_load_factor() c试图维护的平均桶大小,返回float值。c会在需要时添加新的桶,使得load_factor<=max_load_factor。
c.rehsah(n) 重组存储,使得bucket_count >= n且bucket_count>=n。
c.reserve(n) 重组存储,使得c可以保存n个元素且不必rehash。

猜你喜欢

转载自blog.csdn.net/weixin_40313940/article/details/105940453
今日推荐