【STL十一】无序容器(哈希容器)—— unordered_map、unordered_set

news/2024/11/30 9:49:27/

【STL十一】无序容器(哈希容器)—— unordered_map、unordered_set

  • 一、简介
    • 1、关联容器和无序容器不同
    • 2、无序容器特点
  • 二、头文件
  • 三、模板类
    • 四、无序容器的内部结构
    • 1、管理桶
    • 2、内部结构
  • 五、unordered_map成员函数
    • 1、迭代器
    • 2、元素访问
    • 3、容量
    • 4、修改操作
    • ~~5、操作~~
    • 5、查找
    • 6、桶接口
    • 7、哈希策略
  • 六、demo
    • 1、构造函数、emplace、find、迭代器
    • 2、桶接口bucket_count、max_bucket_count、bucket_size、bucket
  • 七、unordered_multimap
  • 八、unordered_set
  • 九、unordered_multiset。

  • 简介
    除了序列式容器和关联式容器之外,C++ 11 标准库又引入了一类容器,即无序容器。无序容器,又称无序关联式容器、或者哈希容器。

和关联式容器一样,此类容器存储的也是键值对元素;不同之处在于,关联式容器默认情况下会对存储的元素做升序排序,而无序关联式容器不会。
和其它类容器相比,无序关联式容器擅长通过指定键查找对应的值,而遍历容器中存储元素的效率不如关联式容器。

在这里插入图片描述

一、简介

无序容器包含有 4 个具体容器,分别为 unordered_map、unordered_multimap、unordered_set 以及 unordered_multiset。

无序容器功能
unordered_map存储键值对 <key, value> 类型的元素,其中各个键值对键的值不允许重复,且该容器中存储的键值对是无序的。
unordered_multimap和 unordered_map 唯一的区别在于,该容器允许存储多个键相同的键值对。
unordered_set不再以键值对的形式存储数据,而是直接存储数据元素本身(当然也可以理解为,该容器存储的全部都是键 key 和值 value 相等的键值对,正因为它们相等,因此只存储 value 即可)。另外,该容器存储的元素不能重复,且容器内部存储的元素也是无序的。
unordered_multiset和 unordered_set 唯一的区别在于,该容器允许存储值相同的元素。

1、关联容器和无序容器不同

和关联式容器一样,无序容器也使用键值对(pair 类型)的方式存储数据。不过,它们有本质上的不同:

  • 关联式容器的底层实现采用的树存储结构,更确切的说是红黑树结构;
  • 无序容器的底层实现采用的是哈希表的存储结构。

2、无序容器特点

基于底层实现采用了不同的数据结构,因此和关联式容器相比,无序容器具有以下 2 个特点:

  • 无序容器内部存储的键值对是无序的,各键值对的存储位置取决于该键值对中的键,
  • 和关联式容器相比,无序容器擅长通过指定键查找对应的值(平均时间复杂度为O(1));但对于使用迭代器遍历容器中存储的元素,无序容器的执行效率则不如关联式容器。

二、头文件

#include <unordered_map>
#include <unordered_set>

三、模板类

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;

四、无序容器的内部结构

1、管理桶

  • 每一个位置,我们把其叫做一个桶。
  • 通过哈希函数映射时可能好几个值,映射到一个桶。
  • 如果桶的数量小的时候,可能会出现挂篮在的现象。(即一个桶下有好几个篮子——存储的对象)。这样我们找某个值时,先找到对应的桶,然在桶里再找我们桶内存储的值。
  • 管理桶:无序容器的性能依赖于哈希函数的质量和桶的数量大小

2、内部结构

中间黑色的代表桶

  • unordered_map 、unordered_multimap在这里插入图片描述
  • unordered_set、unordered_multiset
    在这里插入图片描述

五、unordered_map成员函数

1、迭代器

成员函数功能
begin()同array容器
end()同array容器
rbegin()同array容器)
rend()同array容器)
cbegin()同array容器
cend()同array容器
crbegin()同array容器)
crend()同array容器)

2、元素访问

成员函数功能
operator[]同array容器
at(n)同array容器
front()同array容器
back()同array容器
data()同array容器

3、容量

成员函数功能
size()同array容器
max_size()同array容器
empty()同array容器
reserve同vector容器
capacity同vector容器
shrink_to_fit同vector容器

4、修改操作

成员函数功能
clear()同vector容器
insert()同vector容器
insert_or_assign(C++17)同map容器
emplace()同vector容器
emplace_hint()同map容器
try_emplace(C++17)同map容器
erase()同vector容器
push_back()同vector容器
emplace_back()同vector容器
pop_back()同vector容器
push_front()同vector容器
emplace_front()同vector容器
pop_front()同vector容器
resize()同vector容器
swap()同vector容器
extract(C++17)从另一容器释出结点
merge(C++17)从另一容器接合结点

5、操作

成员函数功能
merge()list容器
splice()list容器
remove(val)list容器
remove_if()list容器
reverse()list容器
unique()list容器
sort()list容器

5、查找

成员函数功能
count(key)同map容器
find(key)同map容器
contains (C++20)同map容器
equal_range(key)同map容器
lower_bound(key)同map容器
upper_bound(key)同map容器

6、桶接口

成员函数功能
begin()同array容器
end()同array容器
cbegin()同array容器
cend()同array容器
bucket_count正在使用的桶的数目。
max_bucket_count()容器容纳的最多的桶的数量
bucket_size(n)第n个桶中有多少个元素
bucket(key)关键字为k的元素在哪个桶种

7、哈希策略

成员函数功能
load_factor每个桶的平均元素数量,返回float值
max_load_factor试图维护的平均桶大小,返回float值,会在需要时添加新的桶,以使得load_facotr < max_load_factor
rehash(n)重组存储,使得bucket_count >=n, 且bucket_count > size/max_load_factor
reserve(n)重组存储,使得可以保存n个元素且不必rehash。

六、demo

1、构造函数、emplace、find、迭代器

  • 返回值
    指向键等于 key 的元素的迭代器。若找不到这种元素,则返回尾后(见 end() )迭代器。
#include <iostream>
#include <string>       // string
#include<unordered_map>using namespace std;
int main() {// 调用构造函数 1,也就是默认构造函数unordered_map <string, string> umap{{"小b","家在西安"},{"小a","家在北京"},{"小d","没有家"},};umap.emplace("小c","家在濮阳" );cout << "i can find 小c:" << endl;auto ite = umap.find("小c");cout << ite->first << "=" << ite->second << endl << endl;//使用迭代器输出 umap 容器存储的所有键值对for (auto iter = umap.begin(); iter != umap.end(); ++iter) {cout << iter->first << " " << iter->second << endl;}return 0;
}

输出

i can find 小c:
小c=家在濮阳


小b 家在西安
小a 家在北京
小d 没有家
小c 家在濮阳
在这里插入图片描述

2、桶接口bucket_count、max_bucket_count、bucket_size、bucket

#include <iostream>
#include <string>       // string
#include<unordered_map>using namespace std;
int main() {// 调用构造函数 1,也就是默认构造函数unordered_map <string, string> umap{{"小b","家在西安"},{"小c","家在濮阳"},{"小a","家在北京"},{"小d","没有家"},};//获取键为 小c的键值对所在的范围auto pair = umap.equal_range("小c");//输出 pair 范围内的每个键值对的键的值for (auto iter = pair.first; iter != pair.second; ++iter) {cout << iter->first << "="<<iter->second;}cout << endl;auto bc = umap.bucket_count();cout << "bucket_count = " << bc << endl;auto bcm = umap.max_bucket_count();cout << "max_bucket_count = " << bcm << endl;auto szie = umap.bucket_size(3);cout << "bucket_size(3) = " << szie << endl;auto pos = umap.bucket("小c");cout << "bucket(\"小c\") = " << pos << endl;return 0;
}

输出

小c=家在濮阳
bucket_count = 8
max_bucket_count = 1152921504606846975
bucket_size(3) = 1
bucket(“小c”) = 3

七、unordered_multimap

和 unordered_map 容器一样,unordered_multimap 容器也以键值对的形式存储数据,且底层也采用哈希表结构存储各个键值对。两者唯一的不同之处在于,unordered_multimap 容器可以存储多个键相等的键值对,而 unordered_map 容器不行。

无序容器中存储的各个键值对,都会哈希存到各个桶(本质为链表)中。而对于 unordered_multimap 容器来说,其存储的所有键值对中,键相等的键值对会被哈希到同一个桶中存储。

  • unordered_multimap 容器模板的定义如下所示:
template < class Key,      //键(key)的类型class T,        //值(value)的类型class Hash = hash<Key>,  //底层存储键值对时采用的哈希函数class Pred = equal_to<Key>,  //判断各个键值对的键相等的规则class Alloc = allocator< pair<const Key,T> > // 指定分配器对象的类型> class unordered_multimap;
  • demo
#include <iostream>
#include <string>       // string
#include<unordered_map>
using namespace std;
int main() {// 调用构造函数 1,也就是默认构造函数unordered_multimap <string, string> ummap{{"小b","家在西安"},{"小c","家在濮阳"},{"小a","家在北京"},{"小d","没有家"},{"小d","也没有家"},};//输出 ummap容器中存储键为 'b' 的键值对的数量cout << ummap.count("小d") << endl;for (auto iter = ummap.begin(); iter != ummap.end(); ++iter) {cout << iter->first << " " << iter->second << endl;cout << "ummap.bucket(iter->first) = " << ummap.bucket(iter->first)<<endl;}cout << "ummap.bucket_count() = " << ummap.bucket_count();return 0;
}
  • 输出

2
小b 家在西安
ummap.bucket(iter->first) = 0
小c 家在濮阳
ummap.bucket(iter->first) = 3
小a 家在北京
ummap.bucket(iter->first) = 1
小d 没有家
ummap.bucket(iter->first) = 2
小d 也没有家
ummap.bucket(iter->first) = 2
ummap.bucket_count() = 8

八、unordered_set

unordered_set 容器具有以下几个特性:

  • 不再以键值对的形式存储数据,而是直接存储数据的值;
  • 容器内部存储的各个元素的值都互不相等,且不能被修改。
  • 不会对内部存储的数据进行排序(这和该容器底层采用哈希表结构存储数据有关,可阅读《C++ STL无序容器底层实现原理》一文做详细了解);

对于 unordered_set 容器不以键值对的形式存储数据,读者也可以这样认为,即 unordered_set 存储的都是键和值相等的键值对,为了节省存储空间,该类容器在实际存储时选择只存储每个键值对的值。

  • unordered_set 容器的类模板定义如下:
template < class Key,            //容器中存储元素的类型class Hash = hash<Key>,    //确定元素存储位置所用的哈希函数class Pred = equal_to<Key>,   //判断各个元素是否相等所用的函数class Alloc = allocator<Key>   //指定分配器对象的类型> class unordered_set;
  • demo
#include <iostream>
#include <string>       // string
#include<unordered_set>
using namespace std;
int main() {unordered_set<string> uset{"小b","小c","小a","小d",};auto ite = uset.insert("小e");cout << *(ite.first) << " " << ite.second << endl;unordered_set<string> set2;set2.insert(uset.begin(), uset.end());for (auto iter = set2.begin(); iter != set2.end(); ++iter) {cout << *iter << endl;}return 0;
}
  • 输出

小e 1
小b
小c
小a
小d
小e

九、unordered_multiset。

unordered_multiset 容器大部分的特性都和 unordered_set 容器相同,包括:

  • unordered_multiset 不以键值对的形式存储数据,而是直接存储数据的值;
  • 该类型容器底层采用的也是哈希表存储结构,它不会对内部存储的数据进行排序;
  • unordered_multiset 容器内部存储的元素,其值不能被修改。

和 unordered_set 容器不同的是,unordered_multiset 容器可以同时存储多个值相同的元素,且这些元素会存储到哈希表中同一个桶(本质就是链表)上。
读者可以这样认为,unordered_multiset 除了能存储相同值的元素外,它和 unordered_set 容器完全相同。

  • unordered_multiset 容器类模板的定义如下:
template < class Key,            //容器中存储元素的类型class Hash = hash<Key>,    //确定元素存储位置所用的哈希函数class Pred = equal_to<Key>,   //判断各个元素是否相等所用的函数class Alloc = allocator<Key>   //指定分配器对象的类型> class unordered_multiset;
  • demo
#include <iostream>
#include <unordered_set>
using namespace std;
int main() {std::unordered_multiset<int> umset{ 1,2,2,2,3,4,5 };cout << "multiset size = " << umset.size() << endl;cout << "multiset count(2) =" << umset.count(2) << endl;//删除容器中所有值为 2 的元素int num = umset.erase(2);cout << "删除了 " << num << " 个元素 2" << endl;//输出容器中存储的所有元素for (auto iter = umset.begin(); iter != umset.end(); ++iter) {cout << *iter << " ";}cout << "umset.bucket_count() = "<< umset.bucket_count();return 0;
}
  • 输出

multiset size = 7
multiset count(2) =3
删除了 3 个元素 2
1 3 4 5 umset.bucket_count() = 8

参考:
1、C++ STL 容器库 中文文档
2、STL教程:C++ STL快速入门
3、https://www.apiref.com/cpp-zh/cpp/header.html
4、https://en.cppreference.com/w/cpp/container


http://www.ppmy.cn/news/45456.html

相关文章

Https详解

文章目录 一. 什么是 Https1. "加密"是什么?2. 对称加密3. 非对称加密4. "中间人攻击" 二. 引入证书理解签名黑客能否伪造证书?黑客能否替换公钥?黑客能否篡改签名?如何查看证书? 一. 什么是 Https https 就是 http 安全层(SSL)–> 用来加密的协…

【华为OD机试真题】字符串解密(javaC++python)100%通过率

字符串解密 知识点数组字符串排序 时间限制:1s空间限制:256MB限定语言:不限 题目描述: 给定两个字符串string1和string2。 string1是一个被加扰的字符串。string1由小写英文字母(‘a’~‘z’)和数字字符 (‘0’~ ‘9’)组成,而加扰字符串由’0’~ ‘9’、‘a’~f’…

编程参考 - C语言中未定义宏的值

在C99标准中&#xff0c;可以查看到说明&#xff1a; 在6. Language -> 6.10 Preprocessing directives -> 6.10.1 Conditional inclusion里&#xff0c;第4点&#xff1a; After all replacements due to macro expansion and the defined unary operator have been pe…

Vue2_01_data_插值

插值语法 {{name}} data: vue实例的数据对象 data中数据变化时将重新渲染容器 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><!--引入vue,引入之后vue.js 创建了一个全局变…

Lambda表达式:简介、语法和用法

Lambda表达式&#xff1a;简介、语法和用法 1. Lambda表达式概述2. Lambda表达式语法3. Lambda表达式用法1.遍历列表并输出每个元素2.筛选列表中的偶数并返回一个新列表3.将字符串转换为大写并返回一个新列表4.计算列表中所有元素的总和5.将列表中的元素转换为键值对并放入Map中…

( “树” 之 DFS) 404. 左叶子之和 ——【Leetcode每日一题】

404. 左叶子之和 给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 示例 1&#xff1a; 输入: root [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中&#xff0c;有两个左叶子&#xff0c;分别是 9 和 15&#xff0c;所以返回 24 示例 2: 输入: root [1]…

01-智能计算系统概述

一、寒武纪开创了深度学习处理器方向 (1)2007:开始智能和芯片交叉研究 (2)2013:国际首个深度学习处理器架构 获得了CCF A类会议ASPLOS’14最佳论文;亚洲首获体系结构四大顶会最佳论文。 (3)2014:国际首个多核深度学习处理器架构 获得 CCF A类会议MICRO’14最佳论…

vue打包之后,可以进行修改配置后端地址、端口等信息方法

前言 用vue-cli构建的项目通常是采用前后端分离的开发模式&#xff0c;也就是前端与后台完全分离&#xff0c;此时就需要将后台接口地址打包进项目中&#xff0c;但是&#xff0c;难道我们只是改个接口地址也要重新打包吗&#xff1f;当然不行了&#xff0c;那就太麻烦了&#…