C++ 中 map 字典与 set 集合的使用

2023-05-16

在 C++ 中,map 是关联容器的一种,关联容器将关联到一起,并使用键来查找值。这与 python 中的字典类型类似,也是用来存储键-值对(key-value) 形式的数据的,其同样具有遍历、插入、查询、删除等功能;set 与 map 类似,set 是一种只有键的集合,下面将逐一介绍。

目录

1、map 类型的声明

2、pair 类型

3、map 数据的遍历

4、添加元素

5、删除元素

6、访问/查询元素

7、set 类型

8、获取关联容器的大小

9、map 与 set 都是有序容器


1、map 类型的声明

若想在 C++ 中使用 map 类型创建字典,则需要包含 map 头文件,如下:

#include <map>

map 类型的定义如下:

map<KeyType, ValueType> dict;

其中,KeyType 代表的数据类型,ValueType 代表的数据类型。KeyType 默认是 const 类型,即键在确认之后为只读数据。另外,在一个 map 数据中,一个键只能出现一次;而在 multimap 类型数据中,一个键可以出现多次,本次主要讨论 map 数据。看下面一个例子:

#include <iostream>
#include <string>
#include <map>

using namespace std;
int main(void)
{
    map<string, int> dict{{"key1", 2}};  // 定义一个 map 类型数据 
    dict["key2"] = 3;  // 创建并赋值
    dict["key3"] ;
    cout << "length of dict: " << dict.size() << endl;  // 长度
    cout << dict["key1"] << endl;  // 查找
    cout << dict["key2"] << endl;
    cout << dict["key3"] << endl;  // 值默认为0 
}

//======= 运行结果如下 ========//
length of dict: 2
2
3
0

例子中首先声明了一个 map 类型数据 dict ,key 的数据类型为 string ,value 的数据类型为 int。初始化时注意一对键值对要放在一对花括号{}中,并用逗号隔开:

{key, value}

如果键 key 对还未出现在 dict 中,下标运算会创建一个新元素,其值默认为 0 。这里创建了三个键值对,一个key 为 字符串 "key1" ,值为 2;key 为 字符串 "key3" 的键值对由于未被赋值,因此其值默认为 0 。

2、pair 类型

上述 dict 字典中存储的每一个数据都是 pair 类型的数据,即 map 数据是一群 pair 类型的集合。一个 pair 保存两数据成员: first 和 second,分别用来存储一个键值对的 ,其中 first 成员是 const 的,这也解释了上面提到的 键在确认之后为只读数据。pair 的标准库类型定义在头文件 utility 中,在定义一个 pair 时,其默认构造函数会对数据成员进行初始化。一个例子如下:

int main(void)
{
    pair<string, int> a_pair{"key", 1};  // 定义一个 pair 并初始化
    cout << a_pair.first << ": " << a_pair.second << endl;
}

// =========== 运行结果 =========== //
key: 1

3、map 数据的遍历

map 数据的遍历主要有两种方法,一个是直接遍历,另一种是使用迭代器的遍历。

1)直接遍历:

#include <iostream>
#include <string>
#include <map>

using namespace std;
int main(void)
{
    map<string, int> dict;  // 定义一个 map 类型数据
    dict["key1"] = 2;  // 创建并赋值
    dict["key2"] = 3;  // 
    dict["key3"] ;     // 默认值为0
    for(auto c : dict)  // 遍历,c++11 特有循环方式
    {
        cout << c.first << ": " << c.second << endl;
    }
}
//============ 运行结果 ============//
key1: 2
key2: 3
key3: 0

这种遍历方式就是 for 循环直接遍历。每次循环都会从 dict 中提取一个 pair 类型的对象存入 c 中以供后续操作。

2)使用迭代器遍历

map 支持 vector 中的 begin、end、cbegin、cend 操作,因此我们可以使用这些函数获取迭代器,然后使用迭代器来遍历 map。begin、end 指向首元素以及尾元素之后位置(one past the last element)的迭代器,而容器中的元素位于 begin 与 end 之间,不包含 end,即是一个左闭右开的区间:[begin,end)。cbegin、cend 是C++11引入的新标准,返回 const_iterator,只能用来读取迭代器所指向内容的数据,不能修改数据 。一个使用迭代器遍历的例子如下:

#include <iostream>
#include <string>
#include <map>

using namespace std;

int main(void)
{   
    // 定义并初始化一个 map 类型数据 
    map<string, int> dict{{"key1", 1}, {"key2", 2}, {"key3", 3}};  
    auto map_it = dict.begin();  // 获取指向首元素的迭代器
    // 判断范围,比较当前迭代器和尾后元素迭代器
    while (map_it != dict.end())
    {
        cout << map_it->first << ": " << map_it->second << endl;
        map_it++;  // 迭代器递增,移动到下一个元素
    }
}

4、添加元素

map 的 insert 成员函数用来添加一个元素或者一个元素范围,emplace 成员函数用来添加单个元素。使用 insert 方法向 map 中添加元素的方法如下,不过常用的方式就是第一种方式,剩下的仨了解下就行。

dict.insert({"key", 1})    // 或者 dict.emplace({"key", 1})
dict.insert(make_pair("key", 1))
dict.insert(pair<string, int>("key", 1))
dict.insert(map<string, int>::value_type("key", 1))

另外对于一个不存在的键的单一元素的 insert 或者 emplace 的插入操作会返回一个 pair ,告诉我们是否添加成功,该 pair 的 first 成员是一个迭代器,指向具有给定关键字的元素,second 成员是一个 bool 值,指出元素是插入成功还是已存在于 dict 中,如果之前不存在,则返回true;如果先前已存在,则值为 false。

int main(void)
{
    map<string, int> dict{{"key1", 1}};  // 定义一个 map
    auto ret1 = dict.insert({"key2", 1}); // 执行插入操作,若 "key"不存在于dict中,则返回
    cout << "attention: ret1.first is a iterator points the element we insert" << endl;
    cout << ret1.first->first << ":" << ret1.first->second << endl;
    cout << "key2 insert successfully? " << ret1.second << endl;
    auto ret2 = dict.insert({"key1", 3}); //  "key"已存在于dict中,则insert什么也不做
    cout << "attention: ret2.first is a iterator points the element we insert" << endl;
    cout << ret2.first->first << ":" << ret2.first->second << endl;
    cout << "key1 insert successfully? " << ret2.second << endl;
}
//========= 运行结果 ==========//
attention: ret1.first is a iterator points the element we insert
key2:1
key2 insert successfully? 1
attention: ret2.first is a iterator points the element we insert
key1:1
key1 insert successfully? 0

例子中的返回值 pair 的完整类型如下:

pair<map<string, int>::iterator, bool>

但是!上面的 insert 方式还是麻烦了点,不如直接使用文章开头说的下标操作如果键 key 对还未出现在 dict 中,下标运算会创建一个新元素,其值默认为 0

当插入一个元素范围时,可以做如下操作,这时没有返回值。

dict.insert(dict2.begin(), dict2.end());

5、删除元素

map 有三个版本的 erase 操作,分别如下:

1、dict.erase(key)

从 dict 中删除 键 为 key 的元素,返回删除元素的数量,对于 map 类型来说为 1。

2、dict.erase(p)

从 dict 中删除迭代器 p 指定的元素。p 必须指向 dict 中一个真实元素且不能等于 dict.end()。返回一个指向 p 之后的元素的迭代器。

3、dict.erase(b, e)

从 dict 中删除迭代器 b 和 e 之间的元素,返回e。

int main(void)
{
    // 定义并初始化一个 map 类型数据 
    map<string, int> dict{{"key1", 1}, {"key2", 2}, {"key3", 3}, {"key4", 4}, {"key5", 5}};  
    int ret1 = dict.erase("key1");  // 删除键为 "key1" 的元素
    cout << "ret1: " << ret1 << endl;
    auto ret2 = dict.erase(dict.begin());  // 使用迭代器
    cout << "(ret2->first:ret2->second) = " << ret2->first << ":" << ret2->second << endl;
    auto ret3 = dict.erase(dict.begin(), dict.end());  // 清空
    cout << "ret3->first: " << ret3->first << endl;
}
//=========== 运行结果 ============//
ret1: 1
(ret2->first:ret2->second) = key3:3
ret3->first:

6、访问/查询元素

map 提供了一些查找元素的操作如下:

1、dict.find(key)
// 返回一个迭代器 ,指向第一个键为 key 的元素,若 key 不存在于 dict 中,则返回 dict.end()
2、dict.count(key)
// 返回键为 key 的元素的数量,对于 map 来说,返回1
3、dict.lower_bound(key)
// 返回一个迭代器,指向第一个键不小于 key 的元素
4、dict.upper_bound(key)
// 返回一个迭代器,指向第一个键大于 key 的元素
5、dict.equal_range(key)
// 返回一个迭代器pair,表示关键字恩于 key 的元素的范围。如果不存在,则pair的两个成员均为
//dict.end()
6、dict.at(key)
// 返回key对应的引用,如果 key 不存在,则会抛出异常
// 另外,对于 const map 的查找只能用 at, 用[]会抛出异常
7、dict[key]
// 使用下标操作访问元素,如果该元素并不存在于 dict 中,则该操作会创建一个元素,
// 并赋初始值为0或空字符,初始值取决于具体的数据类型。

一般来说,2~5的方法多用于 multimap 类型的数据中。如果只是想查询一个元素是否存在于 dict 中,则使用 find 是最好的选择。

另外需要注意的是,如果使用下标操作dict[key]访问元素,如果该元素并不存在于 dict 中,则该操作会创建一个元素,并赋初始值为0或空字符,初始值取决于具体的数据类型。

与下标[ ]访问不同的是,dict.at(key) 操作返回 key 对应的 value 的引用,如果 key 不存在,则会抛出异常。另外,对于 const map 的查找只能用 at, 用[ ]会编译出错

因此,使用什么样的访问方式需要根据我们的使用环境进行选择。

7、set 类型

set 顾名思义就是集合,即每个元素只包含一个关键字,而不是键值对,set 定义在头文件 <set> 中。定义如下:

set<string> mySet = {"this", "is", "a", "set"};

与 map 类似,set 也有 find()、count()、insert()、emplace() 以及迭代器等操作方式。

set的迭代器只能是只读类型的,遍历方法如下:

set<int> iset={0, 1, 2, 3, 4, 5, 6};
auto set_it = iset.begin();
for(; set_it != iset.end(); ++set_it)
    cout << *set_it << endl;
"""
输出:
0
1
2
3
4
5
6
"""

8、获取关联容器的大小

map 和 set 类型的元素个数都可以使用 size() 方法获取。

    // 定义并初始化一个 map 类型数据 
    map<string, int> dict{{"key1", 1}, {"key2", 2}, {"key3", 3}}; 
    set<string> mySet = {"this", "is", "a", "set"}; 
    auto map_it = dict.begin();  // 获取指向首元素的迭代器
    cout << dict.size() << endl;  // 输出 3
    cout << mySet.size() << endl;  // 输出 4

9、map 与 set 都是有序容器

set 和 map 是基于的结构的,并且内部元素是有序的,即是经过排序的:如果键的类型是数字,则按照数字大小进行排序,如果是 string,则按照字典序进行排序。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 中 map 字典与 set 集合的使用 的相关文章

  • C++ 类的构造函数之冒号初始化语法

    在实现类的时候往往需要写一个构造函数用于初始化对象 xff0c 出去一般的函数语法之外 xff0c 还有一种冒号语法 比如 xff0c 下面两种构造函数的写法近似相同 xff1a 常规方法 class A private int index
  • educoder--MapReduce基础实战各关卡通关答案

    第1关 xff1a 成绩统计 任务描述 相关知识 什么是MapReduce 如何使用MapReduce进行运算 代码解释 编程要求 测试说明 任务描述 本关任务 xff1a 使用Map Reduce计算班级中年龄最大的学生 相关知识 为了完
  • Could NOT find ddynamic_reconfigure

    下载ddynamic reconfigure的package 链接https github com pal robotics ddynamic reconfigure tree kinetic devel 解压到catkin ws src空
  • 一位工作了10年的C++程序员总结出这些忠告

    1 可以考虑先学习C 大多数时候 xff0c 我们学习语言的目的 xff0c 不是为了成为一个语言专家 xff0c 而是希望成为一个解决问题的专家 做一个有用的程序员 xff0c 做一个赚钱的程序员 我们的价值 xff0c 将体现在客户价值
  • 新手程序员必学的代码编程技巧

    程序员往往渴望加入的是一支 30 的时间在写代码 xff0c 而70 的时间在喝着咖啡讨论着如何将产品做好 的团队 软件工作应该成为一项技术和艺术融合的高智力活动 xff0c 而项目经理应该是一个高度理解质量 范围和进度客观规律的明白人 x
  • 数学之美—细数 傅里叶变换 原理

    目录 一 傅里叶级数 xff08 Fourier Series FS xff09 的实数域表示 二 傅里叶级数 xff08 Fourier Series FS xff09 的复数域表示 三 傅里叶变换 xff08 FT xff09 的引出
  • C++小知识01 —— 0、‘0’、“0”、“\0”、‘\0’、NULL和nullptr

    可能对于才学完C 的初学者来说 这些概念都很简单 但是把它们放在一起 就真的真的给整不会了 其中最容易混淆的有单引号与双引号的用法 还有NULL与nullptr的用法 下面我会依次用代码文字结合的形式给大家讲解 0 这个就很简单 它就是数字
  • 如何解决Git代码冲突?

    本文主要用的是vscode工具 1 为什么会出现代码冲突问题呢 xff1f 可以理解为就是同一时间几个人更改同一个文件 xff0c git 不知道该听谁的 xff0c 所以就报冲突 xff0c 让开发者自己去选择 xff0c 选取到底用哪个
  • 算法优化工程师

    嵌入式算法移植优化 嵌入式算法移植优化 CUDA编程 嵌入式算法移植优化 CPU GPU TPU NPU都是什么 嵌入式算法移植优化 模型压缩与剪枝 嵌入式算法移植优化 pthread 嵌入式算法移植优化 SIMD编程 xff08 单指令流
  • Elasticsearch7.8.0启动报jdk版本错误

    因为7 8 0版本自带jdk12 xff0c 就可能会出现自己linux环境下的jdk版本较低 启动时老报错 xff0c 这时候就需要干掉当前jdk xff0c 然后启动elasticsearch xff0c 让他去匹配自带的jdk 使用命
  • 2-2进程管理-处理机调度

    文章目录 一 调度的层次二 进程调度的时机 切换与过程 调度方式三 调度器 闲逛进程四 调度算法的评价指标五 调度算法 xff08 一 xff09 先来先服务SCFS xff08 二 xff09 短作业优先算法SJF xff08 三 xff
  • 百度ai开放平台使用方法(附带例子详细步骤)

    百度ai开放平台 1 百度ai开放平台内有众多功能 xff0c 如文字识别 xff0c 语音技术等等内容 xff0c 本文章以身份证识别为例子 xff0c 教大家怎么使用它啦 链接走起 xff1a https cloud baidu com
  • 树莓派4b装完系统后,外接的显示屏刚开始蓝屏,过一会就不亮了是什么原因

    有可能是显示屏的连接或设置问题 xff0c 以下是一些可能导致这种情况的原因及相应的解决方法 xff1a 1 连接问题 xff1a 请确保显示屏与树莓派4B连接的正确 xff0c 建议检查接头是否牢固 另外 xff0c 如果你是通过 HDM
  • Maven报错:The packaging for this project did not assign a file to the build arti fact

    https blog csdn net gao zhennan article details 89713407
  • YOLO-MASK对图像数据集进行清洗

    一 前情介绍 之前在捣鼓yolo系列模型 xff0c 尝试着对coco的羊数据集进行训练 xff0c 可是发现不管怎么调参 xff0c Map一直在60几 xff0c 死活上不了70 后来在一位资深老算法师的指点下 xff0c 才明白 xf
  • 进程,文件,内存简述

    1 进程 一个程序一旦在操作系统里动起来 xff0c 他就成为了一个进程 进程表 xff1a 存放有进程的运行情况以及信息 进程核 xff1a 一个时刻 xff0c 一个进程占用的所有资源 核快照 xff1a 进程在某一时刻的状态 当产生了
  • GNURadio中运行ofdm_rx报错:gr::log :INFO: packet_headerparser_b0 - Detected an invalid packet at item ××

    修改方法 xff1a 减小发送端的乘数因子 但是本着知其然还要知其所以然的学习态度 xff0c 下面就解释下出现这种现象的原因 xff1a 2021 10 28 更新 xff1a 在实际的测试中发现 xff0c 引起该问题的原因还有可能是接
  • 关于改进邮政编码的建议 2018-03-08

    关于改进邮政编码的建议 2018 03 08 邮政法 第二十三条规定 xff1a 用户交寄邮件 xff0c 应当清楚 准确地填写收件人姓名 地址和邮政编码 邮政企业应当在邮政营业场所免费为用户提供邮政编码查询服务 邮政编码由邮政企业根据国务
  • Chapter5 MapReduce

    5 1概述 5 1 1分布式并行编程 MapReduce是一种分布式并行编程框架 在计算机发展史上的 34 摩尔定律 34 xff1a CPU的性能每隔18个月就可以翻一番 然而 xff0c 从2005年起 xff0c 摩尔定律逐渐失效 x
  • 解决Hadoop集群hdfs无法启动DataNode的问题

    问题描述 xff1a 在hadoop启动hdfs的之后 xff0c 使用jps命令查看运行情况时发现hdfs的DataNode并没有打开 笔者出现此情况前曾使用hdfs namenode format格式化了hdfs 如有三个hadoop集

随机推荐