C++ STL库(5)

18. 无序容器

无序容器是C++ 11标准才正式引入到STL标准库中的,与关联式容器一样也是使用键值对的方式存放数据,但与关联式容器有着本质上的不同:

  • 关联式容器的底层采用树存储结构(红黑树)
  • 无序容器的底层实现采用的是哈希表的存储结构

C++ STL 底层采用哈希表实现无序容器时,会将所有数据存储到一整块连续的内存空间中,并且当数据存储位置发生冲突时,解决方法选用的是“链地址法”(又称“开链法”)。无序容器具有已下两个特点:

  • 存放键值对无序,存储位置取决于该键值对中的键
  • 无序容器擅长通过指定键查找对应的值(平均时间复杂度为 O(1))
无序容器 功能
unordered_map 存储键值对 <key, value> 类型的元素,其中各个键值对键的值不允许重复,且该容器中存储的键值对是无序的。
unordered_multimap 和 unordered_map 唯一的区别在于,该容器允许存储多个键相同的键值对。
unordered_set 不再以键值对的形式存储数据,而是直接存储数据元素本身(当然也可以理解为,该容器存储的全部都是键 key 和值 value 相等的键值对,正因为它们相等,因此只存储 value 即可)。另外,该容器存储的元素不能重复,且容器内部存储的元素也是无序的。
unordered_multiset 和 unordered_set 唯一的区别在于,该容器允许存储值相同的元素。

19. 底层实现机制

所有无序容器的底层实现都采用的是哈希表存储结构。更准确地说,是用“链地址法”(又称“开链法”)解决数据存储位置发生冲突的哈希表,整个存储结构下图:

在这里插入图片描述

使用无序容器存储键值对时,会先申请一整块连续的存储空间,但此空间并不用来直接存储键值对,而是存储各个链表的头指针,各键值对真正的存储位置是各个链表的节点。上图中的各个链表称为桶(bucket),每个桶都有自己的编号(从 0 开始)。当有新键值对存储到无序容器中时,整个存储过程分为如下几步:

  • 将该键值对中键的值代入设计好的哈希函数,会得到一个哈希值H
  • 将 H 和无序容器拥有桶的数量 n 做整除运算(即 H % n),该结果即表示应将此键值对存储到的桶的编号
  • 建立一个新节点存储此键值对,同时将该节点链接到相应编号的桶上

负载因子:load_factor() = size() / bucket_count(),负载因子越大,意味着容器越满,即各链表中挂载着越多的键值对,这无疑会降低容器查找目标键值对的效率;反之,负载因子越小,容器肯定越空,但并不一定各个链表中挂载的键值对就越少。最大负载因子超过了默认值,则容器会自动增加桶数(通常是翻一倍,),并重新进行哈希rehash(n),以此来减小负载因子的值

为了更好地管控无序容器底层使用的哈希存储结构,各个无序容器的模板类中都提供如下成员方法:

成员方法 功能
bucket_count() 返回当前容器底层存储键值对时,使用桶的数量。
max_bucket_count() 返回当前系统中,unordered_map 容器底层最多可以使用多少个桶。
bucket_size(n) 返回第 n 个桶中存储键值对的数量。
bucket(key) 返回以 key 为键的键值对所在桶的编号。
load_factor() 返回 unordered_map 容器中当前的负载因子。
max_load_factor() 返回或者设置当前 unordered_map 容器的最大负载因子。
rehash(n) 尝试重新调整桶的数量为等于或大于 n 的值。如果 n 大于当前容器使用的桶数,则该方法会是容器重新哈希,该容器新的桶数将等于或大于 n。反之,如果 n 的值小于当前容器使用的桶数,则调用此方法可能没有任何作用。
reserve(n) 将容器使用的桶数(bucket_count() 方法的返回值)设置为最适合存储 n 个元素的桶数。
hash_function() 返回当前容器使用的哈希函数对象。

20. unordered_map

unordered_map 容器和 map 容器一样,以键值对(pair类型)的形式存储数据,存储的各个键值对的键互不相同且不允许被修改。但由于 unordered_map 容器底层采用的是哈希表存储结构,该结构本身不具有对数据的排序功能,所以此容器内部不会自行对存储的键值对进行排序。

//包含头文件
#include <unordered_map>
//容器模板
template < class Key, //键值对中键的类型
           class T, //键值对中值的类型
       class Hash = hash<Key>, //容器内部存储键值对所用的哈希函数
       class Pred = equal_to<Key>, //判断各个键值对键相同的规则
       class Alloc = allocator< pair<const Key,T> >  // 指定分配器对象的类型
           > class unordered_map;

当无序容器中存储键值对的键为自定义类型时,默认的哈希函数 hash 以及比较函数 equal_to 将不再适用(只适用于基本的数据类型,包含string类型),只能自己设计适用该类型的哈希函数和比较函数,并显式传递给 Hash 参数和 Pred 参数。

1)创建

std::unordered_map<std::string, std::string> umap1;
std::unordered_map<std::string, std::string> umap2 {
   {
   "c","c++"}, {
   "p","python"}};
std::unordered_map<std::string, std::string> umap3(umap2);
std::unordered_map <std::string, std::string > retUmap(){
   
    std::unordered_map<std::string, std::string>tempUmap{
   
        {
   "j","java"} };
    return tempUmap;
}
std::unordered_map<std::string, std::string> umap4(retUmap());
std::unordered_map<std::string, std::string> 
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值