C++ primer 第11章 关联容器

文章目录

  • 使用关联容器
    • map示例
  • 关联容器概述
    • 定义关联容器
      • 关联容器值初始化
      • multimap和multiset
    • 关键字类型的要求
    • pair类型
      • pair上的操作
  • 关联容器操作
    • 关联容器额外的类型别名
    • 关联容器迭代器
      • map迭代器
      • set迭代器
      • 关联容器和算法
    • 添加元素
      • 向map添加元素
      • 检测insert的返回值
      • 使用insert代替下标操作写单词计数程序
    • 删除元素
    • map的下标操作
    • 访问元素
      • multimap查找元素代码示例一:count和find
      • multimap查找元素代码示例二:lower_bound和upper_bound
      • multimap查找元素代码示例三:equal_range函数
  • 无序容器
    • 无序容器管理操作

关联容器和顺序容器有着根本的不同:关联容器中的元素是按关键字来保存和访问的,顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。
两个主要的管理容器类型是map和set。map中的元素是key-value对,set中每个元素只包含一个关键字,set支持高效的关键字查询操作——检查一个给定关键字是否在set中。
在这里插入图片描述

使用关联容器

map示例

	map<string, size_t>word_count;string word;while (cin>>word) {++word_count[word];}for (const auto &wc:word_count) {cout << wc.first << " 出现的次数:" << wc.second << endl;}

输入:

hello
hi
haha
hello
haha
ha
^Z

输出:

ha 出现的次数:1
haha 出现的次数:2
hello 出现的次数:2
hi 出现的次数:1

关联容器概述

关联容器不支持顺序容器的位置相关的操作,例如push_front或push_back。关联容器也不支持构造函数或插入操作这些接受一个元素值和一个数量值的操作。

定义关联容器

关联容器值初始化

set<string>st={"set","the","hello"};
map<string,string>mp={{"zhang","san"},{"wang","wu"},{"li","si"}};

multimap和multiset

multimap和multiset允许关键字重复。

关键字类型的要求

关联容器对其关键字类型有一些限制。对于有序容器——map、multimap、set以及multiset,关键字类型必须定义元素比较的方法。
例如,我们不能直接定义Sales_data的multiset,因为Sales_data没有<运算符,但是可以用compareIsbn函数来定义一个multiset,次函数在Sales_data对象的ISBN成员上定义了一个严格弱序。

bool compareIsbn(const Sales_data &lhs,const Sales_data &rhs){return lhs.isbn()<rhs.isbn();
}
multiset<Sales_data,decltype(compareIsbn)*>bookstore(compareIsbn);

此处我们使用decltype来指出自定义操作的类型。当用decltype来获得一个函数指针类型时,必须加上一个*来指出我们要使用一个给定函数类型的指针。用compareIsbn来初始化bookstore对象,表示当我们向bookstore添加元素时,通过调用compareIsbn来为这些元素排序。可以用compareIsbn代替&compareIsbn作为构造函数的参数,因为当我们使用一个函数的名字时,在需要的情况下它会自动转化为一个指针,当然,使用&compareIsbn的效果也是一样的。

pair类型

标准库类型pair定义在头文件utility中。
一个pair保存两个数据成员,类似容器,pair是一个用来生成特定类型的模板。
例如: pair<string,int>p;
与其他标准库类型不同,pair的数据成员是public的,两个成员名分别为first和second。

pair上的操作

在这里插入图片描述

关联容器操作

关联容器额外的类型别名

在这里插入图片描述

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

v5是一个pair<const string ,int>,关键字是const,不能改变

关联容器迭代器

map迭代器

当解引用一个关联容器迭代器时,我们会得到一个类型为容器的value_type的值的引用。下述代码不能执行语句
it->first=“newKey”,因为关键字是const!!
但可执行语句 it->second = 10;

	map<string, int>mp{ { "a",1 },{ "b",2 },{ "c",3 } };auto it = mp.begin();//(*it).first与it->first等价//迭代器it即为一个pair类型cout << (*it).first<< " "<<(*it).second << endl;cout << it->first << " " << it->second << endl;//可以通过迭代器更改元素it->second = 10;cout << it->first << " " << it->second << endl;it++;cout << it->first << " " << it->second << endl;

输出结果:

a 1
a 1
a 10
b 2

set迭代器

与不能改变一个map元素的关键字一样,set中的关键字也是const的,可以用一个set迭代器来读取元素的值,但不能修改:

	set<int>st{1,3,5,7,9,2,3,4,6,5};auto it = st.begin();while (it!=st.end()) {//*it = 100;  此句错误,关键字是const的,只能读不能修改cout << *it << " ";it++;}

输出结果:

1 2 3 4 5 6 7 9

关联容器和算法

我们通常不对关联容器使用泛型算法。关键字是const这一特性意味着不能将关联容器传递给修改或重排容器元素的算法,因为这类算法需要向元素写入值,而set类型中的元素是const的,map中的元素是pair,且其第一个成员是const的。
关联容器可用于只读取元素的算法,但是很多这类算法都要搜索序列。关联容器定义了一个名为find的成员,它通过一个给定的关键字直接获取元素,我们可以用泛型find算法来查找一个元素,但此算法会进行顺序搜索。使用关联容器定义的专用的find成员会比调用泛型find快得多。
在实际编程中,如果我们真要对一个关联容器使用算法, 要么是将它当做一个源序列,要么当做一个目的位置。例如可以用泛型copy算法将元素从一个关联容器拷贝到另一个序列。类似的,可以调用inserter将一个插入器绑定到一个关联容器。通过使用inserter,我们可以将关联容器当做一个目的位置来调用另一个算法。

添加元素

向map添加元素

向map用insert添加元素的四种方法:

map<string,int>wc;
string words="hello";
wc.insert({words,1});
wc.insert(make_pair(words,1));
wc.insert(pair<string,int>(words,1));
wc.insert(map<string,int>::value_type(words,1));

在这里插入图片描述

检测insert的返回值

添加单一元素的insert和emplace版本返回一个pair,pair的first成员是一个迭代器,指向具有给定关键字的元素;second成员是一个bool值,指出元素是插入成功还是已经存在于容器中,如果插入成功,则返回true,否则返回false。

	map<string,int>mp{ { "b",2 },{ "a",1 },{ "c",3 } };auto it1 = mp.insert({ "b",10 });//it1是一个paircout << it1.second<<endl;auto mp1 = it1.first;  //mp1是一个迭代器cout << mp1->first << " "<<mp1->second << endl;auto it2 = mp.insert({ "a2",10 });cout << it2.second << endl;auto mp2 = it2.first;cout << mp2->first << " " << mp2->second << endl;

输出结果:

0
b 2
1
a2 10

第一次插入失败,insert返回的迭代器指向map已存在的元素。
第二次插入成功,insert返回的迭代器指向插入的元素。

而对于允许重复的multimap,insert操作返回一个指向新元素的迭代器,无需返回bool值,因为insert总是向这类容器中加入一个新元素。

使用insert代替下标操作写单词计数程序

	map<string, int>mp;string str;while (cin>>str) {auto ret = mp.insert({ str,1 });if (!ret.second) {auto tmp = ret.first;(tmp->second)++;}}for (auto mp3 : mp) {cout << mp3.first << " " << mp3.second << endl;}

输入:

hello
hi
haha
hahaha
haha
hi
hello
hello
helll
hehehe
^Z

输出:

haha 2
hahaha 1
hehehe 1
helll 1
hello 3
hi 2

删除元素

关联容器定义了三个版本的erase,如图所示:
在这里插入图片描述
与顺序容器一样,我们可以通过传递给erase一个迭代器或一个迭代器对来删除一个元素或者一个元素范围。

	map<string, int>mp{ { "b",2 },{ "a",1 },{ "c",3 } };auto it =mp.erase("a");//将{ "a",1 }删除,it是删除的元素的数量,此处是1
	map<string, int>mp{ { "b",2 },{ "a",1 },{ "c",3 } };auto it =mp.erase(mp.begin());//将mp.begin()指向的元素删除,it是一个迭代器指向删除元素之后元素的迭代器//此处mp.begin()指向{ "a",1 },it指向{ "b",2 }

map的下标操作

map和unodered_map容器提供了下标运算符和一个对应的at函数,set类型不支持下标,因为set中没有与关键字相关联的“值”。我们不能对一个multimap或一个unodered_multimap进行下标操作,因为这些容器中可能有多个值与一个关键字相关联。
在这里插入图片描述

map下标运算符接受一个索引(即,一个关键字),获取与此关键字相关联的值。但是,与其他下标运算符不同的是,如果关键字并不在map中,会为它创建一个元素并插入到map中,关联值将进行值初始化。例如下述代码,mp["d"];使得mp中增加了元素{ “d”,0 }。

	map<string, int>mp{ { "b",2 },{ "a",1 },{ "c",3 } };for (auto mp3 : mp) {cout << mp3.first << " " << mp3.second << endl;}cout << endl;mp["d"];for (auto mp3 : mp) {cout << mp3.first << " " << mp3.second << endl;}

输出结果:

a 1
b 2
c 3a 1
b 2
c 3
d 0

由于下标运算符可能插入一个新元素,我们只可以对非const的map使用下标操作。
与vector和string不同,map的下标运算符返回的类型(mapped_type)与解引用map迭代器得到的类型(value_type)不同。

访问元素

在这里插入图片描述
在这里插入图片描述

multimap查找元素代码示例一:count和find

给定一个作者到著作的映射,打印一个特定的作者的所有著作

string item("luxun");
auto entries = authors.count(item);
auto iter = authors.find(item);
while(entries){cout<<iter->second<<endl;++iter;--entries;
}

multimap查找元素代码示例二:lower_bound和upper_bound

可以用lower_bound和upper_bound解决此问题。lower_bound返回的迭代器将指向第一个具有给定关键字的元素,而upper_bound返回的迭代器则指向最后一个匹配给定关键字的元素之后的位置。因此用相同的关键字调用lower_bound和upper_bound会得到一个迭代器范围,表示所有具有该关键字的元素。如果关键字不存在,则lower_bound和upper_bound指向相同的位置,迭代器范围即为空。
重写代码示例一的程序如下:

for(auto beg = authors.lower_bound(item),end=authors.upper_bound(item);
beg!=end;++beg){cout<<beg->second<<endl;
}

multimap查找元素代码示例三:equal_range函数

equal_range:接受一个关键字,返回一个迭代器pair,第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置,若未找到匹配元素,则两个迭代器都指向关键字可以插入的位置,即upper_bound返回的迭代器的位置。

for(auto pos = authors.equal_range(item);
pos.first!=pos.second;++pos.first){cout<<pos.first->second<<endl;
}

无序容器

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

无序容器管理操作

在这里插入图片描述

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

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

相关文章

C++ primer 第12章 动态内存

文章目录前言动态内存与智能指针shared_ptr类shared_ptr和unique_ptr都支持的操作shared_ptr独有的操作make_shared 函数shared_ptr的拷贝和赋值shared_ptr自动销毁所管理的对象shared_ptr还会自动释放相关联的内存程序使用动态内存出于以下原因直接管理内存使用new动态分配和初…

C语言顺序查找二分查找

介绍 顺序查找 按照顺序一个个查找 #include<stdio.h> //顺序查找 int search(int arr[],int len,int aim) {int i;for(i0;i<len;i){if(arr[i]aim){return i;//返回下标 }}return -1;//表示未查询到} int main() {int arr[]{13,355,256,65,234,-1,35,-6,-3,-4,0};…

C++ primer 第12章 12.3 使用标准库:文本查询程序

文章目录使用标准库&#xff1a;文本查询程序文本查询程序设计数据结构在类之间共享数据自己的文本查询程序书中的文本查询程序使用标准库&#xff1a;文本查询程序 我们将实现一个简单的文本查询程序&#xff0c;作为标准库相关内容学习的总结。 我们的程序允许用户在一个给…

C语言二维数组 int arr[2][3]

基础使用 先遍历行再遍历列 #include<stdio.h> //二维数组的基本使用 int main() {//二维数组的初始化int arr1[2][2]{{2,2},{0,0}};int arr2[2][3]{2,2,2,8,8,8};int arr3[6][9];int i,j;for(i0;i<6;i){for(j0;j<9;j){arr3[i][j]1;}}arr3[2][5]0;//打印printf(&…

C++ primer 第13章 拷贝控制

文章目录前言拷贝、赋值与销毁拷贝构造函数合成拷贝构造函数拷贝初始化和直接初始化拷贝初始化的发生&#xff1a;参数和返回值拷贝初始化的限制拷贝赋值运算符重载赋值运算符合成拷贝赋值运算符析构函数析构函数完成的工作什么时候会调用析构函数合成析构函数代码片段调用几次…

C语言 指针自增自减加减运算 p++ p+i

介绍 自增自减代码 #include<stdio.h> #include<string.h> //指针自增--short void increase(short *arr,int len) {int i;arr&arr[0];for(i0;i<len;i){printf("arr[%d]%d,address%p\n",i,*arr,arr);arr;} }//指针自减--char void decrease(char…

C++ 编译与底层

原文链接 编译与底层请你来说一下一个C源文件从文本到可执行文件经历的过程&#xff1f; 对于C源文件&#xff0c;从文本到可执行文件一般需要四个过程&#xff1a;预处理阶段&#xff1a;对源代码文件中文件包含关系&#xff08;头文件&#xff09;、预编译语句&#xff08;…

C语言 指针数组-字符指针数组整型指针数组 char*s[3] int*a[5] 数组指针int(*p)[4]

基本介绍 1.指针数组:由n个指向整型元素的指针而组成,里面存放指针 Int *ptr[3]; 2.地址: ptr[i]:元素地址 &ptr[i]:指针地址 图示 代码: 内存布局: 代码 #include<stdio.h> #include<string.h> //指针数组--int void pointer(int *arr,int len) {int …

C语言 多重指针--整型字符字符串 int**pp

介绍 多重指针:一个指针指向另一个指针 离值越近的指针级别越大:一级 内存布局 代码 图示: 多重指针–整型 #include<stdio.h> #include<string.h> //多重指针--整型//二级指针 void two() {printf("二级指针:\n");int a896;int *p&a,**pp&…

C++ primer 第13章 拷贝控制

文章目录前言拷贝、赋值与销毁拷贝构造函数合成拷贝构造函数拷贝初始化和直接初始化拷贝初始化的发生&#xff1a;参数和返回值拷贝初始化的限制拷贝赋值运算符重载赋值运算符合成拷贝赋值运算符析构函数析构函数完成的工作什么时候会调用析构函数合成析构函数代码片段调用几次…

牛客网C++面经 C++11

请问C11有哪些新特性&#xff1f; auto关键字&#xff1a;编译器可以根据初始值自动推导出类型。但是不能用于函数传参以及数组类型的推导nullptr关键字&#xff1a;nullptr是一种特殊类型的字面值&#xff0c;它可以被转换成任意其它的指针类型&#xff1b;而NULL一般被宏定义…

C语言 返回指针的函数--指针函数 int* max(int a)

定义 strlong示例代码 代码1: #include<stdio.h> #include<string.h> //返回指针的函数//比较两个字符串,返回更长的字符串 char *strlong(char* a,char* b) {char *p1&a[0];char *p2&b[0];while(true){if(*p1\0){return b;}else if(*p2\0){return a;}p1…

第2、3讲 图像的存储格式

本图像处理系列笔记是基于B站杨淑莹老师的课程进行学习整理的。 文章目录黑白图像8位灰度索引图像8位伪彩色索引图像24位真彩色图像图像文件格式BMP文件存储格式BMP文件头位图信息头颜色表位图信息——BITMAPINFO结构BMP位图文件汇总按照颜色深度分类&#xff0c;常用图像文件&…

Ubuntu18.04.4 环境下对属性加密算法CP-ABE环境搭建

注意事项 cpabe依赖pbc&#xff0c;pbc依赖gmp&#xff0c;gmp依赖M4、bison、flex如果权限不够 &#xff0c;命令的前面加上sudo &#xff0c;不要直接使用root用户进行操作&#xff0c;其带来的隐患有很多 第一步 配置简单的环境 简单环境 包括gcc、g、make、cmake、openss…

C语言 函数指针 int(*ptr)(int,int)

基本介绍 函数指针:指向函数的指针 与数组类似 定义 Int(*pmax)(int ,int)max; Int(*pmax)(int x,int y)max;//形参名称不重要 函数返回类型(*指针)(形参类型)函数名称; 具体案例 代码: *pmax取到函数本身 调用函数指针方式: (*pmax)(x,y); pmax(x,y);//与java中调用函数一…

C++ primer 第14章 操作重载与类型转换

文章目录基本概念直接调用一个重载的运算符函数某些运算符不应该被重载使用与内置类型一致的含义选择作为成员或者非成员输入和输出运算符重载输出运算符<<输出运算符尽量减少格式化操作输入输出运算符必须是非成员函数重载输入运算符>>算术和关系运算符相等运算符…

C语言 回调函数 produce(arr,len,getRand)

基本介绍 回调函数:形参中包含另一个函数的函数指针 用函数指针接收另一个函数 案例 代码解析 具体代码 #include<stdio.h> #include<stdlib.h> //回调函数--//函数原型 int getRand(); int *produce(int*arr,int len,int(*get)()); int main() {int arr[10…

C语言 动态内存分配机制(堆区) int*p=malloc(5*sizeof(4))

C程序内存分配图 栈区:局部变量 堆区:动态分配的数据 静态存储区/全局区:全局变量,静态数据 代码区:代码,指令 内存分配说明 内存动态分配的相关函数 堆区: #inlcude<stdlib.h> Malloc(size);//分配长度为size个字节的连续空间 Calloc(n,size);//分配size个长度为n…

C语言 结构体 struct Cat cat1;

引入 使用传统技术解决 需要定义多个变量或数组 结构体与结构体变量的关系示意图 类似Java类中的对象(结构体)与属性(结构体变量) 一切物体都可以看作对象(结构体) 补充:C语言数据类型 简单使用案例 代码 Cat是我们自己定义的数据类型 struct Cat cat1;//创建struct Cat的…

C语言 共用体/联合体 union

引入 传统技术的缺陷—结构体 共用体基本介绍 共用体与结构体一样都是值传递 定义共用体的三种方式 内存布局 共用体数据空间占用最大的成员的数据空间大小 案例解析 1) 2) 3) 4) 注: 1010 1101 0101 0100所对应的十进制是负数 计算机中的二进制都是以补码存储的,所…