C++_map_set详解

news2024/9/20 14:32:39

关联容器的基本介绍

关联容器支持高效的关键字查找和访问。map和set是最主要关联容器。关联容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。C++标准库中提供了8个关联容器:

关联容器的类型
按关键字有序保存元素
map关联数组;保存键值对
set关键字即值,即只保存关键字的容器
multimap关键字可重复出现的map
multiset关键字可重复出现的set
无序集合
unordered_map用哈希函数组织的map
unordered_set用哈希函数组织的set
unordered_multimap哈希组织的map;关键字可重复出现
unordered_multiset哈希组织的set;关键字可重复出现

其中,类型map和multimap定义在头文件map中;set和multiset定义在头文件set中;无序容器则定义在头文件unordered_map和unordered_set中。本文重点讨论map与set

键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key相对应的信息。比如 可以将一个人的名字作为关键字key,将其电话号码作为值value,这样我们就可以通过人名来找到对应的电话号码。

pair类型 

pair是一个标准库类型,它定义在头文件utility中。pair文档

一个pair保存两个数据成员。类似容器,pair是一个用来生成特定类型的模板,下面是pair的原型

// SGI-STL中关于键值对的定义
template <class T1, class T2>
struct pair
{
    typedef T1 first_type;
    typedef T2 second_type;
    
    pair()                            // 构造
        : first(T1())
        , second(T2())
    {}
    pair(const T1& a, const T2& b)    // 拷贝构造
        : first(a)
        , second(b)
    {}

    T1 first;                         // 成员变量
    T2 second;
};

 当创建一个pair时,我们必须提供两个类型名,pair的数据成员将具有对应的类型

// 代码示例
pair<string, string> a;         // 保存两个string
pair<string, int> b;            // 保存一个string和一个int
pair<string, vector<int>> c;    // 保存一个string和vector<int>

 pair的默认构造函数会对数据成员进行值初始化。因此a是一个包含两个空string的pair;b中的int成员值初始化为0,而string成员初始化为空;c保存了一个空string和一个空的vector。

我们也可以为每个成员提供初始化器:

// 创建一个名为object的pair,两个成员分别被初始化为“xxxx”和2
pair<string, int> object {"xxxx", 2};
pair的常见操作
pair<T1, T2> p;p是一个pair,两类型分别为T1和T2的成员进行了 值初始化
pair<T1, T2> p (v1, v2);p是一个成员类型为T1和T2的pair;first和second成员分别用v1,v2初始化
pair<T1, T2> p = {v1, v2};等价于 p (v1, v2)
make_pair(v1, v2);返回一个用v1和v2初始化的pair。pair的类型从v1和v2的类型推断出来

set

set的基本介绍 

  1. set是按照一定次序存储元素的容器
  2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
  3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格的排序准则(默认是小于)进行排序。
  4. set在底层是用二叉搜索树(红黑树)实现的。

set的基本使用

set的构造

函数声明功能介绍
set (const Compare& comp = Compare(), const Allocator&
= Allocator() );
构造空的set

set (InputIterator first, InputIterator last, const Compare&

comp = Compare(), const Allocator& = Allocator() );

用[first, last)区间中的元素构造set
set ( const set<Key,Compare,Allocator>& x);set的拷贝构造
// set构造
std::set<int> first;                                  // empty set of ints

int myints[]= {10,20,30,40,50};
std::set<int> second (myints,myints+5);               // range
std::set<int> third (second);                         // a copy of second
std::set<int> fourth (second.begin(), second.end());  // iterator ctor.

set的迭代器

虽然set同时定义了iterator和const_iterator类型,但两种类型都只允许只读访问set中的元素,因为set中的关键字是const的。可以用一个set迭代器来读取元素的值,但不能修改

set<int> s = {0,1,2,3,4,5,6,7,8,9};
set<int>::iterator it = s.begin();
if(it != s.end())
{
    //*it = 20;              // 错误,set中的关键字是只读的
    cout << *it << endl;     // 正确,可以读关键字
}

set的修改操作

函数声明功能介绍
pair<iterator,bool> insert (const value_type& k )在set中插入元素k,实际插入的是<k, k>构成的
键值对,如果插入成功,返回<该元素在set中的
位置,true>,如果插入失败,说明k在set中已经
存在,返回<k在set中的位置,false>
void erase ( iterator position )删除set中position位置上的元素
void erase ( iterator first, iterator last )删除set中[first, last)区间中的元素
size_type erase ( const key_type& k )删除set中值为k的元素,返回删除的元素的个数
插入操作

insert操作向容器中添加一个元素或一个元素范围。

vector<int> v = {2,4,6,8,2,4,6,8};
set<int> s;
s.insert(v.begin(), v.end());        // 迭代器
s.insert({1,3,5,7,1,3,5,7});         // 初始化器列表

insert有两个版本,分别接受一对迭代器,或是一个初始化器列表,这两个版本的行为类似对应的构造函数。 

删除操作 

set定义了三个版本的erase,如上表所示。与vector(序列式容器)一样,我们可以通过传递给erase一个迭代器 或者 一个元素范围。这两个版本的erase与对应的序列式容器的操作非常相似:指定元素被删除,函数返回void。

set提供了一个额外的erase操作,此版本删除所有匹配给定关键字的元素(如果存在的话),返回实际删除的元素的数量。就set来说此版本的erase的返回值总是0或者1。若返回0则说明该元素不存在,返回1则说明存在;但是如果在multiset上面,返回值可能会大于1。

set的访问操作

函数说明功能简介
iterator find ( const key_type& k ) const

返回一个迭代器,指向第一个关键字为 k 的元素,

若k不在容器中,则返回尾后迭代器end()

size_type count ( const key_type& k ) const返回关键字等于 k 的元素的数量。
set<int> s = {0,1,2,3,4,5,6,7,8,9};
s.find(1);            // 返回一个迭代器,指向key == 1的元素
s.find(20);           // 返回一个迭代器,其值等于s.end()
s.count(1);           // 返回1
s.count(20);          // 返回0

map

map的基本介绍

  1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
  2. 在map中,键值key通常用于排序和唯一地标识元素,而值value中存储与此键值key关的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair:typedef pair<const key, T> value_type;
  3. 在内部,map中的元素总是按照键值key进行比较排序的。
  4. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
  5. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。

map的基本使用

map的构造

函数声明功能介绍
map()构造一个空的map
map (const map& x);拷贝构造

map的迭代器 

注意:map的value_type是一个pair类型,其first成员保存const的关键字,second成员保存值。我们可以改变pair的值,但是不能改变关键字成员的值!!!

map<string, string> m;
// 使用insert插入元素到m
m.insert(pair<string, string>("peach", "桃子"));
m.insert(make_pair("banan", "香蕉"));

map<string, string>::iterator it = m.begin();
// 遍历map
while(it != m.end())
{
    cout << it->first << " " << it->second << endl;// 打印此元素的关键字和值
    //it->first = "apple"                          // 错误,关键字是const
    it->second++;                                  // 正确,可以通过迭代器改变值
}

map的修改操作

函数声明功能简介
pair<iterator,bool> insert (const value_type& k )在map中插入键值对k,注意 k 是一个键值
对,返回值也是键值对:iterator代表新插入
元素的位置,bool代表释放插入成功
void erase ( iterator position )删除map中position位置上的元素
void erase ( iterator first, iterator last )删除map中[first, last)区间中的元素
size_type erase ( const key_type& k )删除map中值为k的元素,返回删除的元素的个数
插入操作

对map进行insert操作时,必须要记住元素类型是一个pair。通常,对于想要插入的数据,并没有一个现成的pair对象。但是可以在insert的参数列表中创建一个pair。

// pair的构造方式
map<string, string> dict;
// 方式一:有名对象
pair<string, string> kv("left", "左边");
dict.insert(kv);
// 方式二:匿名对象
dict.insert(pair < string, string>("right", "右边"));
// 方式三:make_pair函数模板(make_pair帮你构造)
dict.insert(make_pair("up", "上边"));
// 方式四:多参数构造函数-隐式类型转换--中间生成临时对象
pair<string, string> kv1 = { "down", "下边" };
dict.insert({ "down", "下边" });
// 方式五:initializer_list构造
map<string, string> dict = { {"left", "左边"}, {"right", "右边"}, {"up", "上边"} ,{"down", "下边"} };
删除操作

参考set的删除操作

map的访问操作

函数声明功能简介
iterator find ( const key_type& k ) 

返回一个迭代器,指向第一个关键字为 k 的元素,

若k不在容器中,则返回尾后迭代器end()

size_type count ( const key_type& k ) const

返回key为k的键值在map中的个数,注意map中key

是唯一的,所以该函数的返回值要么为0,要么为1,

因此也可以用该函数来检测一个key是否在map中

mapped_type& operator[] (const
key_type& k)
返回key对应的value

ps. 其中find函数与count函数请参见set

再谈insert

前面我们已经了解过insert函数作用和使用场景,下面我们再来谈谈insert的返回值。

        仔细观察insert的函数声明不难发现,其返回值是一个pair。其中pair的first成员是一个迭代器,指向指定关键字的元素;second成员是一个bool值,指出元素是插入成功还是已经存在容器中。如果关键字已在容器中,则insert什么也不做,且其中的bool值为false;如果关键字不存在,元素则被插入到容器中,且bool值为true。

// 单词计数程序
map<string, int> m;
string word;
while(cin >> word)
{
    // 插入一个元素,关键字等于word, 值为1
    // 若word已经在m中,则insert什么也不做
    auto ret = m.insert({word, 1});
    if(!ret.second)                         // word已经在m中
    {
        ++((ret.first)->second);            // 递增计数器
    }
}

上面代码中的if语句检查返回值的bool部分,若为false,则说明插入操作并未发生。

明确了insert函数,下面我们再来谈谈map的下标操作operator[]。

operator[]

map和unordered_map容器提供了下标运算符,简单说,operator[]就是给我一个key,我返回key对应value的引用。operator[]文档

上面两张图非常详细的介绍了operator[]的基本原理,希望大家能够理解

// 代码演示
map<string, int> m;
m["kevin"] = 1;
// 将会进行下面的操作
// 在m中搜索关键字为kevin的元素,未找到。
// 将一个新的键值对插入到m中,关键字是一个const string,保存kevin。值进行初始化为0
// 提取出新插入的元素,并赋值为1

对于map的下标操作,其行为与数组或vector上的下标操作很不相同:使用一个不在容器中的关键字作为下标,会添加一个具有此关键字的元素到map中

map的下标操作与其他下标运算符的另一个不同之处就是其返回类型。通常情况下,解引用一个迭代器所返回的类型与下标运算符返回的类型是一样的。但map的下标操作会返回一个key对应value的引用;当解引用map的迭代器时,会得到一个pair对象。

与其他下标运算符相同的是,map的下标运算符返回一个左值。由于返回的是一个左值,所以我们可以读写元素。

cout << m["kevin"];        // 用Kevin作为下标提取元素,会打印出1
++m["kevin"];              // 提取元素,将其增加1
cout << m["kevin"];        // 提取元素并打印,会打印出2

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

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

相关文章

如何关闭前端Chrome的debugger反调试

1、禁用浏览器断点 2. 把控制台独立一个窗口

如何优雅地处理返回值

我们已经知道了如何优雅的校验传入的参数了&#xff0c;那么后端服务器如何实现把数据返回给前端呢&#xff1f; 返回格式 后端返回给前端我们一般用 JSON 体方式&#xff0c;定义如下&#xff1a; {#返回状态码code:string, #返回信息描述message:string,#返回值data…

《Google软件测试之道》笔记

介绍 GTAC&#xff1a;Google Test Automation Conference&#xff0c;Google测试自动化大会。 本书出版之前还有一本《微软测试之道》&#xff0c;值得阅读。 质量不是被测试出来的&#xff0c;但未经测试也不可能开发出有质量的软件。质量是开发过程的问题&#xff0c;而不…

09年408考研真题解析-计算机网络

[题34]在无噪声情况下&#xff0c;若某通信链路的带宽为3kHz&#xff0c;采用4个相位&#xff0c;每个相位具有4种振幅的QAM调制技术,则该通信链路的最大数据传输速率是&#xff08;B&#xff09; A.12 kbps B.24 kbps C.48 kbps D.96 kbps 解析&#xff…

优惠充值话费api对接如何选择对接平台?

优惠充值话费接口通常由电信运营商、第三方支付平台或专业的充值服务提供商提供。这些平台通过API接口允许开发者将话费充值功能集成到应用程序或网站中。 选择哪个平台比较好&#xff0c;取决于以下几个因素&#xff1a; 覆盖范围&#xff1a;选择能够覆盖你需要服务的地区和…

49.面向对象综合训练-朋友

1.创建JavaBean类 public class Friend {//题目要求&#xff1a;定义数组存储4个朋友对象//属性&#xff1a;姓名&#xff0c;年龄&#xff0c;性别&#xff0c;爱好//计算出四位朋友的平均年龄//统计出比平均年龄低的朋友有几个&#xff0c;并把信息都打印出来private String…

openstack之keystone介绍

功能 keystone在OpenStack中负责&#xff1a; 管理&#xff1a;用户、租户和权限&#xff1b; 认证&#xff1a;组件相互访问的身份认证&#xff1b; 鉴权&#xff1a;提供 RBAC&#xff08;Role Based Access Control&#xff09; 权限体系&#xff1b; 服务注册与发现&#…

windows系统docker装milvus向量数据库

首先创建一个文件夹比如milvus,在创建如下文件 docker-compose.yml文件如下: version: 3.5services:etcd:container_name: milvus-etcdimage: quay.io/coreos/etcd:v3.5.5environment:- ETCD_AUTO_COMPACTION_MODErevision- ETCD_AUTO_COMPACTION_RETENTION1000- ETCD_QUOTA_B…

Redis重要知识点:哨兵是什么?哨兵如何选择Redis主服务器

引言 哨兵贼简单&#xff0c;就是一个节点去看守领一个节点有没有挂掉&#xff0c;挂掉的数量比较多那得新选主节点了。 1. Redis哨兵 1.1 哨兵作用 哨兵的含义是什么&#xff1f;我们来看看百度百科的解释。 哨兵&#xff0c;汉语词语&#xff0c;是指站岗、放哨、巡逻、稽…

关于less的基本使用

1、介绍及概述 1.1、解释 less 是方便开发人员书写CSS的一门预处理语言。浏览器只认识html /css /js格式的文件&#xff0c;所以直接引入.less文件&#xff0c;没有任何的效果&#xff0c;需要把less文件转换成css文件 1.2、概述 CSS弊端&#xff1a; 没有逻辑性、变量、函…

【Paper Reading】结合 NanoFlow 研究,优化大语言模型服务效率的探索

作者 王伟 PAI引擎团队 近年来&#xff0c;人工智能领域的快速发展推动了大型语言模型的广泛应用&#xff0c;随之而来的是对其服务效率的迫切需求。论文《NanoFlow&#xff1a;Towards Optimal Large Language Model Serving Throughput》提出了一种突破性的新型服务框架&…

刚接触无处下手?水下航行器AUV/UUV六自由度模型/控制器设计matlab/simulink参考代码,基础的/进阶的,入门到顺利毕业/完成课题/发表论文。

导师不管&#xff1f;无人指导&#xff1f;无代码可参考&#xff1f;毫无头绪&#xff1f;换条思路借鉴一下吧&#xff0c;金钱买不到时间&#xff0c;但可以让你更多的支配你自己的时间&#xff0c;没错的&#xff0c;条条大路通罗马&#xff0c;毕竟前程是自己的&#xff0c;…

时序必读论文11|ICLR23 TimesNet时间序列分析的二维变化建模

论文标题&#xff1a;TEMPORAL 2D-VARIATION MODELING FOR GENERAL TIME SERIES ANALYSIS 开源代码&#xff1a;https://github.com/Thinklab-SJTU/Crossformer 前言 时间序列分析中&#xff0c;如何有效地建模时序数据中的时间变化是关键&#xff0c;然而直接从一维时序数据…

css <样式一>

1. 盒子模型 1.1>boarder 在这里插入图片描述 boarder 相邻框合并问题 boarder-classpse 相同的边框会合并在一起 text-alicn center 文字居中对齐 ########### boarder 会撑大盒子的实际大小 一个盒子加了boarder之后会变大的我可以把我的盒子内容进行修改, 减少像素内…

细胞分裂检测系统源码分享

细胞分裂检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

大数据Flink(一百二十):Flink SQL自定义函数(UDF)

文章目录 Flink SQL自定义函数&#xff08;UDF&#xff09; 一、概述 二、​​​​​​​自定义标量函数&#xff08;UDSF&#xff09; 三、​​​​​​​​​​​​​​自定义聚合函数(UDAF) 四、 ​​​​​​​​​​​​​​自定义表值函数(UDTF) Flink SQL自定义函数…

re题(20)BUUCTF [GWCTF 2019]pyre

BUUCTF在线评测 (buuoj.cn) Python解包及反编译: PyInstaller Extractoruncompyle6 - 知乎 (zhihu.com) python撤消&#xff1a; Pycharm撤销操作和代码跳转后退回操作以及消除波浪线操作快捷键_pycharm怎么反撤销-CSDN博客 把.pyc文件变成py文件 把.py文件用记事本打开 cod…

supermap iclient3d for cesium模型沿路径移动

可以直接settimeout隔一段时间直接设置位置属性&#xff0c;但是得到的结果模型不是连续的移动&#xff0c;如果想要连续的移动&#xff0c;就需要设置一个时间轴&#xff0c;然后给模型传入不同时间时的位置信息&#xff0c;然后就可以了。 开启时间轴 let start Cesium.Jul…

负载均衡:从理论到实践 ---day04

负载均衡 负载均衡1.什么是负载均衡2.负载均衡的分类硬件负载均衡软件负载均衡选择 3.引入负载均衡的好处 第一个Ribbon实例步骤1&#xff1a;步骤2&#xff1a;步骤3&#xff1a;步骤4&#xff1a; 问题1. 负载均衡的主要目标是什么&#xff1f;2. 负载均衡器的作用是什么&…

【云岚到家-即刻体检】-day07-2-项目介绍及准备

【云岚到家-即刻体检】-day07-2-项目介绍及准备 1 项目介绍1&#xff09;项目简介2&#xff09;界面原型3&#xff09;实战目标 2 搭建实战环境1&#xff09;服务端2&#xff09;管理端前端工程3&#xff09;用户端前端工程4&#xff09;测试 3 熟悉项目代码1&#xff09;接口文…