C++(11):关联容器

news2024/12/29 9:43:58

关联容器和顺序容器有着根本的不同:关联容器中的元素是按关键字来保存和访问的。与之相对,顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。

虽然关联容器的很多行为与顺序容器相同,但其不同之处反映了关键字的作用。

关联容器支持高效的关键字查找和访问。两个主要的关联容器类型是 mapset

map 中的元素是一些关键字-值(key-value)对:关键字起到索引的作用,值则表示与索引相关联的数据。
set 支持高效的关键字查询操作——检查一个给定关键字是否在 set 中。

类型 mapmultimap 定义在头文件 map 中;setmultiset 定义在头文件 set 中;无序容器则定义在头文件 unordered_mapunordered_set 中。

在这里插入图片描述

使用关联容器

关联数组的下标不必是整数。

使用 map

//统计每个单词在输入中出现的次数
map<string,size_t> word_count; // string 到size_t的空map
string word;
while (cin >>word)
	++word_count[word];//提取 word 的计数器并将其加1
for (const auto &w : word_count)//对map中的每个元素
	//打印结果
	cout << w.first << " occurs " << w.second
			<< ((w.second > 1) ? " times":" time")<< endl;

使用 set

//统计输入中每个单词出现的次数
map<string,size_t> word_count;// string到size_t的空 map
set<string> exclude ={"The","But", "And","or","An""A",
									"the","but","and", "or", "an", "a"};
string word;
while (cin >> word)
	//只统计不在exclude中的单词
	if (exclude.find(word)==exclude.end())
		++word_count[word];//获取并递增word的计数器

关联容器概述

1.关联容器不支持顺序容器的位置相关的操作,例如 push_front 或 push_back。因为,关联容器中元素是根据关键字存储的,这些操作对关键容器没有意义。

2.关联容器也不支持构造函数或插入操作。
3.关联容器的迭代器都是双向的

定义关联容器

定义 map 时,必须既指明关键字类型又指明值类型:{key,value}
定义 set 时,只需要指明关键字类型。因为 set 中没有值,元素类型就是关键字的类型。

每个关联容器都定义了一个默认构造函数,它创建一个指定类型的空容器。也可以将关联容器初始化为另一个同类型容器的拷贝,或者是从一个值范围来初始化关联容器,只需要这些值可以转换为容器所需类型就可以。

map<string,size_t> word count;//空容器//列表初始化
set<string> exclude = {"the","but","and","or","an","a","The","But","And" , "or", "An","A"};
//三个元素;authors 将姓映射为名
map<string,string> authors = { {"Joyce", "James"},
													{"Austen", "Jane"},
													{ "Dickens", "Charles"} };

一个mapset 中的关键字必须是唯一的,即,对于一个给定的关键字,只能有个元素的关键字等于它。容器multimapmultiset没有此限制,它们都允许多个元素具有相同的关键字。

关键字类型的要求

默认情况下,标准库使用关键字类型的 < 运算符来比较两个关键字。在集合类型中,关键字类型就是元素类型;在映射类型中,关键字类型是元素的第一部分的类型。

传递给排序算法的可调用对象必须满足与关联容器中关键字一样的类型要求。

有序容器的关键字类型

可以向算法提供一个自己定义的比较操作,操作必须在关键字类型上定义一个严格弱序,类似小于等于但不一样:
1.两个关键字不能同时”小于等于“对方。
2.该操作有传递性。
3.如果两个关键字互不”小于等于“对方,那么两个就是等价的。容器将它们看做相等。
如果两个关键字是等价的(即,任何一个都不“小于等于”另一个),那么容器将它们视作相等来处理。
当用作map 的关键字时,只能有一个元素与这两个关键字关联,可以用两者中任意一个来访问对应的值。

使用关键字类型的比较函数

为了使用指定自定义的操作,必须在定义关联容器类型时提供此操作的类型。自定义的操作类型(函数指针类型)必须在尖括号中紧跟元素类型给出。
比较函数应该返回 bool 值,两个参数的类型应该都是容器的关键字类型。
当用 decltype 来获得一个函数指针类型时,必须加上一个 * 来指出用使用一个给定函数类型的指针。

pair 类型

pair 标准库类型,定义在头文件 utility 中。一个 pair 保存两个数据成员,是一个用来生成特定类型的模板。
创建一个 pair 时,必须提供两个类型名,pair 的数据成员将具有对应的类型。两个类型可以不一样:

pair<string,string>anon;//保存两个string
pair<string,size_t> word_count;//保存一个string和一个size_t
pair<string,vector<int>> line;//保存string和vector<int>

pair 上的操作
在这里插入图片描述
创建 pair 类型的函数

pair<string,int>
process(vector<string> &v){
	//处理 v
	if(!v.empty())
		return (v.back(),v.back().size());//列表初始化
	else
		return pair<string,int>();//隐式构造返回值
}

可以使用 make_pair 来生成pair 对象,pair 的两个类型来自于 make_pair 的参数:

if (!v.empty())
	return make_pair(v.back(), v.back().size());

关联容器操作

关联容器额外的类型别名
在这里插入图片描述不能改变一个元素的关键字,因此pair的关键字部分是 const 的:

set<string>:: value_type v1;//v1 是一个 string
set<string>:: key_type v2;//v2 是一个 string
map<string,int>:: value_tupe v3;//v3 是一个 pair<const string,int>
map<string,int>:: key_type v4;//v4 是一个 string
map<string,int>:: mapped_type v5;//v5 是一个 int

关联容器迭代器

当解引用一个关联容器迭代器时,会得到一个类型为容器的 value_type 的值的引用。对 map 而言,value_type 是一个 pair 类型,其 first 成员保存 const 的关键字,second 成员保存值。可以改变 pair 的值,但不能改变关键字成员的值。在这里插入图片描述

//获得指向word count中一个元素的迭代器
auto map it=word count.begin();
// *map it是指向一个pair<const string,size_t>对象的引用
cout << map it->first;//打印此元素的关键字
cout << " "<<map it->second;//打印此元素的值
map_it->first = "new key";//错误:关键字是const的
++map_it->second;//正确:我们可以通过迭代器改变元素

set 的迭代器是 const 的,可以用 set 的迭代器读取元素值,但不能修改。

关联容器中的元素不能通过它们的关键字(快速)查找。
在实际编程中,如果我们真要对一个关联容器使用泛型算法,要么是将它当作一个源序列,要么当作一个目的位置。
例如,可以用泛型 copy 算法将元素从一个关联容器拷贝到另一个序列。也可以用 inserter 将一个插入器绑定到另一个关联容器。

添加元素

关联容器 insert 操作
在这里插入图片描述

mapset (以及对应的无序类型)包含不重复的关键字,因此插入一个已知存在的元素对容器没有任何影响。

vector<int> ivec = {2,4,6,8,2,4,6,8;// ivec有8个元素
set<int> set2;//空集合
set2.insert(ivec.cbegin(), ivec.cend());//set2有4个元素
set2.insert({1,3,5,7,1,3,5,7});//set2现在有8个元素

向 map 添加元素

对一个 map 进行 insert 操作时,元素类型必须是 pair

//向word_count插入word的4种方法
word_count.insert({word,1});
word_count.insert (make_pair(word,1));
word_count.insert (pair<string,size_t> (word, 1));
word_count.insert (map<string,size_t>::value_type (word,1));

检测 insert 的返回值

insert (或 emplace)返回的值依赖于容器类型和参数。
对于不包含重复关键字的容器,添加单一元素的 insertemplace 版本返回一个 pair ,这个 pairfirst 成员是一个迭代器,指向具有给定关键字的元素;second 成员是一个 bool 值,指出元素是插入成功还是已经存在于容器中:
已经存在,则insert 什么也不做,bool 部分为 false;不存在,元素被插入容器中,bool 部分为 true

//统计每个单词在输入中出现次数的一种更烦琐的方法
map<string,size_t> word_count;1/从 string到size_t的空mapstring word;
while (cin>>word) {
	//插入一个元素,关键字等于word,值为1;
	//若word已在word count中, insert什么也不做
	auto ret = word_count.insert({word,1});
	if(!ret.second)//word已在word_count中
		++ret.first->second;//递增计数器
}

//ret 保存 insert 返回的值,是一个 pair。
//ret.first 是 pair 的第一个成员,是一个 map 迭代器,指向具有给定关键字的元素。
//ret.first->解引用此迭代器,提取 map 中的元素,元素也是一个 pair。
//ret.first->second map 中元素的值部分。
//++ret. first->second 递增此值。

multisetmultimap 上调用 insert 总是可以插入元素,因为它们允许关键字出现多次。

删除元素

定义了三种 erase() 函数来从关联容器删除元素:
在这里插入图片描述
常用操作:

//删除一个关键字,返回删除的元素数量
if(word_count.erase(removal_word))
	cout<<"ok : "<<removal_word<< "removed\n";
else 
	cout<<"oops: "<<removal_word<<" not found!\n";

对于保存不重复关键字的容器,erase 的返回值总是0或1。若返回值为0,则表明想要删除的元素并不在容器中。

map 的下标操作

map 和 unordered_map 的下标操作
在这里插入图片描述

不能对一个multimap或一个unordered_multimap进行下标操作,因为这些容器中可能有多个值与一个关键字相关联。

类似其他下标运算符,map 下标运算符接受一个索引(即,一个关键字),获取与此关键字相关联的值。但是,与其他下标运算符不同的是,如果关键字并不在map中,会为它创建一个元素并插入到 map 中,关联值将进行值初始化。

当对一个map进行下标操作时,会获得一个mapped_type对象;但当解引用一个map迭代器时,会得到一个value_type对象。

访问元素

对于不允许重复关键字的容器,使用 count 和 find 没什么区别,因为只有一个。
在这里插入图片描述lower_boundupper_bound 不适用于无序容器。
下标和 at 操作只适用于非 constmapunordered_map

可以对 map 使用 find 代替下标操作:

// 检查一个元素是否存在
if (word_count.find("foobar")== word_count.end())
	cout << "foobar is not in the map" <<endl;

如果一个 multimapmultiset 中有多个元素具有给定关键字,则这些关键字在容器中会相邻存储。
三种不同的关键字查找方法:
1.使用 findcount。使用 count 确定有多个元素,然后调用 find 获得一个迭代器,指向第一个关键字为此关键字的元素,for循环的迭代次数依赖于 count 的返回值。
2.使用 lower_boundupper_bound 得到一个迭代器范围,表示所有具有该关键字的元素的范围。
3.使用 equal_range 函数。此函数接受一个关键字,返回一个迭代器 pair。若关键字存在,则第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置。若未找到匹配元素,则两个迭代器都指向关键字可以插入的位置。

无序容器

4 个无序关联容器不是使用比较运算符来组织元素,而是使用一个哈希函数和关键字类型的 == 运算符。
如果关键字类型固有就是无序的,或者性能测试发现问题可以用哈希技术解决,就可以使用无序容器。

除了哈希管理操作之外,无序容器还提供了与有序容器相同的操作(find,insert等)。
无序容器也有允许重复关键字的版本。

通常可以用无序容器替换对应的有序容器,反之亦然。但是,无序容器中元素未按顺序存储。

管理桶

无序容器在存储上组织为一组桶,每个桶保存零个或多个元素。无序容器使用一个哈希函数将元素映射到桶。
为了访问一个元素,容器首先计算元素的哈希值,它指出应该搜索哪个桶。容器将具有一个特定哈希值的所有元素都保存在相同的桶中。
如果容器允许重复关键字,所有具有相同关键字的元素也都会在同一个桶中。

对于相同的参数,哈希函数必须总是产生相同的结果。理想情况下,哈希函数还能将每个特定的值映射到唯一的桶。但是,将不同关键字的元素映射到相同的桶也是允许的。

当一个桶保存多个元素时,需要顺序搜索这些元素来查找我们想要的那个。计算一个元素的哈希值和在桶中搜索通常都是很快的操作。但是,如果一个桶中保存了很多元素,那么查找一个特定元素就需要大量比较操作。

无序容器管理操作
在这里插入图片描述

无序容器对关键字类型的要求

默认情况下,无序容器使用关键字类型的==运算符来比较元素,它们还使用一个hash<key_type>类型的对象来生成每个元素的哈希值。标准库为内置类型(包括指针)提供了hash模板,还包括 string 和智能指针也提供了 hash 模板。

不能使用默认的 hash,需要自定义 hash 模板来定义关键字类型为自定义类类型的无序容器。

//hasher函数使用一个标准库hash类型对象来计算ISBN成员的哈希值,该hash类型建立在string类型之上。e
size_t hasher(const Sales_data &sd){
	return hash<string>()(sd.isbn());
}
//qOp函数通过比较ISBN号来比较两个Sales_data。
bool eqOp(const Sales_data &lhs,const Sales_data &fhs){
	return lhs.isbn() == rhs.isbn();
}

重要术语

哈希函数(hash function) 将给定类型的值映射到整形(size_t)值的函数。相等的值必须映射到相同的整数:不相等的值应尽可能映射到不同整数。
严格弱序(strict weak ordering) 关联容器所使用的关键字间的关系。在一个严格弱序中,可以比较任意两个值并确定哪个更小。若任何一个都不小于另一个,则认为两个值相等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/686161.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

服务器数据中了locked勒索病毒,有关locked勒索病毒的介绍与预防建议

随着网络的普及和科技技术的发展&#xff0c;网络安全问题日益突出。而其中&#xff0c;勒索病毒就是一种常见的网络安全威胁。一旦企业的服务器数据库被勒索病毒攻击&#xff0c;会导致企业内部的重要数据被加密&#xff0c;给工作和生产生活带了极大的困扰。下面就为大家介绍…

Python工具箱系列(三十七)

二进制文件操作&#xff08;上&#xff09; python比较擅长与文本相关的操作。但现实世界中&#xff0c;对于非文本消息的处理也很普遍。例如&#xff1a; ◆通过有线、无线传递传感器获得的测量数据。 ◆卫星通过电磁波发送测量数据。 ◆数据中心的数万台服务器发送当前CP…

Android Studio 配置 DCL 单例脚本

DCL&#xff08;Double-Checked Locking&#xff09;单例是一种用于创建单例对象的设计模式。单例模式是一种创建型模式&#xff0c;用于确保一个类只有一个实例&#xff0c;并提供全局访问点。 DCL单例的核心思想是使用双重检查来保证只有在需要时才对实例进行实例化。它结合…

MIT 6.830 数据库系统 -- Lab One

MIT 6.830 Lab One 项目拉取SimpleDB存储结构一览SimpleDB特性说明Lab One练习一练习二练习三练习四练习五练习六练习七 项目拉取 原项目使用ant进行项目构建&#xff0c;我已经更改为Maven构建&#xff0c;大家直接拉取我改好后的项目即可: https://gitee.com/DaHuYuXiXi/si…

物联网助力鲜花冷链安全——温湿度监控系统

近几年来我国花卉生产的发展尤为迅速&#xff0c;生产面积逐年扩大&#xff0c;产值成倍增长&#xff0c;内销市场越来越旺&#xff0c;出口创汇也有较大幅度上升。 随着人民生活水平的提高和可支配收入的增加&#xff0c;人们对鲜花的需求日益增长&#xff0c;花卉市场的前景…

电子药盒语音芯片ic解决方案WT588F02B-8S,免屏实现精准较时设定

概述&#xff1a;电子药盒是一种具备定时语音提醒服药的贴心智能家居用品&#xff0c;每天的服药时间是预先设定好的&#xff0c;到了设定的时间提醒声音就会响起&#xff0c;服药者因此就可以准时服药。许多需要每天服药的人士&#xff0c;尤其是老年人群体&#xff0c;经常会…

Matlab论文插图绘制模板第105期—带缺口的分组填充箱线图

在之前的文章中&#xff0c;分享了Matlab带缺口的分组箱线图的绘制模板&#xff1a; 进一步&#xff0c;再来分享一下带缺口的分组填充箱线图的绘制模板。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;本期内容『数据代码』已上传资源群中&#xff0c;加群的朋友请自…

算法leetcode|59. 螺旋矩阵 II(rust重拳出击)

文章目录 59. 螺旋矩阵 II&#xff1a;样例 1&#xff1a;样例 2&#xff1a;提示&#xff1a; 分析&#xff1a;题解&#xff1a;rust&#xff1a;go&#xff1a;c&#xff1a;python&#xff1a;java&#xff1a; 59. 螺旋矩阵 II&#xff1a; 给你一个正整数 n &#xff0c…

高速PCB设计中串行/并行信号的仿真与设计技巧

在高速PCB设计中&#xff0c;串行和并行信号的仿真是为了确保信号传输的准确性和完整性&#xff0c;随着系统数据传输速度的提高&#xff0c;准确模拟和优化信号的传输性能对于系统稳定性至关重要&#xff0c;那么你知道在高速PCB设计中&#xff0c;如何针对串行信号和并行信号…

jmeter夸线程组变量引用

通过BeanShell 后置处理程序引用函数&#xff1a; ${__setProperty(newvar,${oldvar},)}如下&#xff1a; 说明&#xff1a;HTTP_1返回结果msg的值为{“code”:200&#xff0c;“msg”:“操作成功”} 1.通过JSON提取器获取到要跨线程组的参数值 2.BeanShell后置处理程序引用…

杂谈:人到中年总得有点兴趣爱好

写在最前面&#xff1a; 前面一段写的是我咋喜欢的铜钱&#xff0c;后面一段是我对学习和转化的一点儿见解&#xff0c;有兴趣可以看看&#xff0c;甚至可以跳过前面去看看后面的内容~ 前几天跟朋友吃饭聊天&#xff0c;不知不觉中大家都到了中年&#xff0c;也都有点儿兴趣爱好…

SAP从入门到放弃系列之工艺路线-物料分配与组件分配-part1

文章概览 一、概述&#xff1a;二、基本概念&#xff1a;2.1、物料分配概览2.1.1物料适用场景&#xff1a;2.1.2物料分配方式&#xff1a; 2.2、组件分配概览2.2.1 组件适用场景2.2.2 组件分配注意事项&#xff1a; 三、测试示例3.1、准备工艺路线组数据&#xff1a;3.2、工艺路…

【Java】数组中的拷贝方法与初步理解深浅拷贝

文章目录 普通的数组拷贝函数可以指定区间拷贝的数组拷贝函数深浅拷贝 普通的数组拷贝函数 Arrays.copyOf(array,array.length) 我们进入到这个函数的源码中&#xff0c;可以看到这两个参数一个是原始数组&#xff0c;一个是拷贝后的新的长度。 例如&#xff1a; public cla…

YOLOv5图像和视频对象生成边界框的目标检测实践(GPU版本PyTorch错误处理)

识别图像和视频里面的对象&#xff0c;在计算机视觉中是一个很重要的应用&#xff0c;比如无人驾驶&#xff0c;这个就需要实时的检测到周边环境的各种对象&#xff0c;并及时做出处理。目标检测在以往的文章中有重点讲解过几种&#xff0c;其中Faster R-CNN的源码解读&#xf…

数据结构之复杂度分析

1、大 O 复杂度表示法 算法的执行效率&#xff0c;粗略地讲&#xff0c;就是算法代码执行的时间 这里有段非常简单的代码&#xff0c;求 1,2,3…n 的累加和。看如何来估算一下这段代码的执行时间 int cal(int n) {int sum 0;int i 1;for (; i < n; i) {sum sum i;}ret…

Vue3版本生命周期详解

介绍 vue3和vue2的生命周期改动不大,下面以图来展现两个版本的周期钩子 使用示例 配置项写法 vue3可以使用vue2版本的周期配置 准备一个HelloWord组件 使用App组件嵌套HelloWorld组件,并进行v-if判断是否卸载该组件,以此查看vue3的卸载钩子 测试: 可以看到当页面刷新后执行…

Maven的安装与使用

一、简介 1.什么是Maven? Maven翻译为“专家“&#xff0c; ”内行”的意思&#xff0c;是著名Apache公司下基于Java开发的开源项目。Maven项目对象模型&#xff08;POM&#xff09;是一个项目管理工具软件&#xff0c;可以通过简短的中央信息描述来管理项目的搭建&#x…

前端Vue自定义滚动卡片,可以用于商品海报生成

前端Vue自定义滚动卡片&#xff0c;可以用于商品海报生成&#xff0c; 下载完整代码请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13189 实现代码如下&#xff1a; # cc-scroolCard #### 使用方法 使用方法 <!-- dataInfo&#xff1a;滚动…

(1)ADS-B接收机

文章目录 前言 1.1 所需硬件 1.2 连接到自动驾驶仪 1.3 设置 1.4 ADSB输出配置 1.5 启用载人飞行器避障功能 1.6 飞行器数据库 1.7 开发者信息包括模拟 前言 本文介绍了如何安装和配置 ADS-B 模块&#xff0c;以便你的飞机能够知道附近的其他飞机和空中交通管制&#…

高性能消息中间件 RabbitMQ

一、RabbitMQ概念 1.1 MQ是什么 消息队列 MQ全称Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中保存消息的容器。多用于系统之间的异步通信。 同步通信相当于两个人当面对话&#xff0c;你一言我一语。必须及时回复&#xff1a; 异步通信相…