关联容器
-
关联容器支持高效的关键字查找和访问,两个主要关联容器map和set。
①map中元素是key-value对,关键字起索引的作用,值表示与索引相关联的数据。
②set中每个元素只包含关键字。 -
标准库提供8个关联容器:
①map、set,有序,关键字不可重复。
②unordered_map、unordered_set,无序,关键字不可重复。
③multimap、multiset,有序,关键字可重复。
④unordered_ multimap、unordered_multiset,无序,关键字可重复。 -
无序容器由哈希函数组织元素。
-
关联容器支持普通容器操作,不支持顺序容器位置相关的操作如push_back和push_front。
-
关联容器的迭代器都是双向的。
关键字类型的要求
-
有序容器关键字必须定义元素的比较方法。
①默认情况下,标准库使用<运算符来比较两个关键字。
②传递给排序算法可调用对象的元素必须满足与关联容器关键字一样的类型。 -
可向算法提供自己定义的操作代替关键字上的<运算符,所提供定义的操作必须在关键字类型上严格弱序。
pair类型
-
pair是生成特定类型的模板,一个pair保存两个成员,类似容器。
-
pair的构造函数对数据成员值初始化,与其他标准库类型不同,pair的数据成员是public,两个成员分别命名为first和second,用普通成员访问符号来访问。
-
map的元素是pair,first成员保存关键字,second成员保存相应的值。
①pair<T1, T2> p; p是一个成员类型分别为T1和T2的pair,成员值初始化。
②pair<T1, T2> p(v1, v2); p的 frist和second成员分别用v1和v2初始化。
③pair<T1, T2> p = {v1, v2}; 等价于p(v1, v2)。
④make_pair(v1, v2); 返回用v1和v2初始化的pair,成员类型可由v1和v2类型推断。
⑤p.first; 返回p的名为first的公有数据成员。
⑥p.second; 返回p的名为second的公有数据成员。
⑦p1 relop p2; 关系运算符(<、>、<=、>=)按字典序定义,first和second成员都满足时,pair满足,利用成员元素的运算符实现。
⑧p1 == p2; 当first和second成员分别相等时,pair相等。
⑨p1!= p2; 相等及不等判断利用元素的==运算符实现。
关联容器操作
-
关联容器额外类型别名:
①key_type,容器类型的关键字类型。
②mapped_type,关键字关联的类型,只适用于map。
③value_type,对set,与key_type相同;对map,为pair<const key_type, mapped_type>。 -
关联容器迭代器
①解引用关联容器迭代器时,会得到容器类型value_type值的引用。
②map的value_type是pair类型,first成员保存const的关键字,second成员保存值。
③set的迭代器是const的。 -
通常不对关联容器使用泛型算法:
①关键字const的特性不能将关联容器传递给修改或重排容器元素的算法。
②关联容器的元素不能通过关键字快速查找,不可用泛型搜索算法。
③关联容器的find成员可通过给定关键字直接获取元素,实际编程中,可将关联容器当作源序列或目的序列使用此算法。
添加、删除元素
-
map和set,当元素关键字不在c中时插入元素,函数返回一个pair,包含指向关键字元素的迭代器,以及插入是否成功的bool值;对multimap和multiset总会插入给定元素,并返回指向新元素的迭代器。
①c.insert(v); v是value_type类型的对象。
②c.emplace(args); args构造新元素。
③c.insert(p, v); 类似insert(v)或emplace(args),迭代器p指出从哪里开始搜索新元素存储位置。
④c.emplace(p, args); args构造元素,从p处搜索新元素插入位置。 -
map和set,插入关键字不在c中的元素,multimap和multiset插入范围中所有元素。
①c.insert(b, e); b和e是迭代器,表示一个c::value_type类型值的范围。
②c.insert(il); il是c::value_type类型值的花括号列表,函数返回void。 -
删除元素
①c.erase(k); 从c中删除所有关键字为k的元素,返回一个size_type的值表删除数量。
②c.erase( p); 从c中删除迭代器p指向的元素,不可为尾后迭代器,返回p之后元素的迭代器,若p为c中尾元素,则返回c.end()。
③c.erase(b, e); 删除迭代器对b和e范围内元素,返回e。
map和unordered_map的下标操作
-
c[k]; 返回关键字为k的元素,若k不在c中,添加关键字为k的元素,并值初始化。
①返回左值可读可写。
②下标运算符可能插入新元素,只可对非const的map使用下标操作。 -
c.at(k); 访问关键字为k的元素,若k不在c中,抛出一个out_of_range导常。
访问元素
-
lower_bound和upper_bound以及equal_bound不适用于无序容器。
①c.lower_bound(k); 返回指向第一个关键字不小于k的元素的迭代器。
②c.upper_bound(k); 返回指向第一个关键字大于k的元素的迭代器。
③c.equal_bound(k); 返回迭代器pair,表示关键字等于k的元素范围,若k不存在,pair两成员相等,指向关键字安全插入点。 -
如果关键字在容器中,lower_bound返回指向第一个关键字元素的迭代器,upper_bound返回最后一个关键字元素之后位置的迭代器。
-
如果关键字不在容器,equal_bound返回相等的迭代器,指向关键字第一个安全插入点,即不影响排序的关键字插入位置。
-
下标操作只适用于非const的map和unordered_map。
①c.find(k); 返回迭代器,指向第一个关键字为k的元素,k不在容器中返回尾后迭代器。
②c.count(k); 返回关键字等于k的元素数量,对于不重复关键字的容器返回值永远是0或1。
无序容器
-
无序容器不使用比较运算符组织元素,使用哈希函数和关键字类型的==运算符,理论上哈希技术能获得更好的平均性能。
①默认情况下,无序容器使用关键字类型的 == 运算符比较元素。 -
使用hash<key_type>类型的对象生成每个元素的哈希值,可直接定义关键字是以下类型的无序容器。
①标准库为内置类型包括指针提供了hash模板。
②同时为包括string和智能指针在内的标准库类型定义了hash。 -
不能直接定义关键字为自定义类型的无序容器,需为自定义类型重载==运算符和哈希值计算函数。
-
如果关键字类型本身就是无序的,或者性能测试发现问题可以用哈希技术解决,就可以使用无序容器。
管理桶
-
无序容器在存储组织上为一组桶,每个桶保存零个或多个元素,无序容器使用哈希函数将元素映射到桶。
①c.bucket_count(); 正在使用的桶的数目。
②c.max_bucket_count(); 容器能容纳的最多的桶的数量。
③c.bucket_size(n); 第n个桶中有多少个元素。 -
当访问一个元素时,容器先计算元素的哈希值,它指出应该搜索哪个桶。
①c.bucket(k); 关键字为k的元素在哪个桶中。 -
容器将具有同一个哈希值的元素保存在相同的桶中,相同的参数,哈希函数产生相同的结果,同时允许将不同关键字的元素映射到相同的桶中。
-
无序容器的性能依赖于哈希函数的质量和桶的数量和大小。
①c.load_factor(); 每个桶的平均元素数量,返回float值。
②c.max_load_factor(); c试图维护的平均桶的大小,返回float值,c会在需要时添加新的桶,使得load_factor<=max_load_factor。
③c.rehash(n); 重组存储,使得bucket_count>=n且 bucket_count>size/max_load_factor。
④c.reserve(n);重组存储,使得c可以保存n个元素且不必rehash。 -
访问桶中元素的迭代器:
①local_iterator; 访问桶中元素的迭代器类型。
②const_local_iterator; 桶迭代器的const类型。
③c.begin(n), c.end(n); 桶n的首元素迭代器和尾后迭代器。
④c.cbegin(n), c.cend(n); const版本。