C++笔记---set和map

news2024/12/25 12:31:35

1. 序列式容器与关联式容器

前面我们已经接触过STL中的部分容器如:string、vector、list、deque、array、forward_list等,这些容器统称为序列式容器,因为逻辑结构为线性序列的数据结构,两个位置存储的值之间一般没有紧密的关联关系,比如交换⼀下,他依旧是序列式容器。

顺序容器中的元素是按他们在容器中的存储位置来顺序保存和访问的。

关联式容器也是用来存储数据的,与序列式容器不同的是,关联式容器逻辑结构通常是非线性结构,两个位置有紧密的关联关系,交换一下,他的存储结构就被破坏了。

顺序容器中的元素是按关键字来保存和访问的。

关联式容器有map/set系列和unordered_map/unordered_set系列。

本章节讲解的map和set底层是红黑树,红黑树是一颗平衡二叉搜索树。

set/multiset是key搜索场景的结构, map/multimap是key/value搜索场景的结构。

set/map不支持相同的值插入,multiset/multimap支持相同的值插入。

2. pair类 

在正式介绍set和map之前,我们需要先了解一下pair类。

这个类也是一个类模板,顾名思义,它的作用就是存储两个数据,也即将两个数据绑定到一起。

在需要返回两个数据的场景下,我们就可以将两个数据存到pair中进行返回。

pair的原型如下:

template <class T1, class T2>
struct pair
{
	typedef T1 first_type;
	typedef T2 second_type;

	T1 first;// 第一个数据
	T2 second;// 第二个数据
    
    // 默认构造
	pair() : first(T1()), second(T2())
	{}
    
    // 直接传入两个数据进行构造
	pair(const T1& a, const T2& b) : first(a), second(b)
	{}
    
    // 支持隐式类型转换
	template<class U, class V>
	pair(const pair<U, V>& pr) : first(pr.first), second(pr.second)
	{}
};

// C++11之间常用的构造pair的函数
template <class T1, class T2>
inline pair<T1, T2> make_pair(T1 x, T2 y)
{
	return (pair<T1, T2>(x, y));
}

3. set的介绍

参考文档:set - C++ Reference,关于set的使用,将其当作key搜索场景的红黑树来使用即可。

set的原型:

template < class T,                        // set::key_type/value_type
           class Compare = less<T>,        // set::key_compare/value_compare
           class Alloc = allocator<T>      // set::allocator_type
           > class set;

类型参数分别为:key的类型,比较器(通过仿函数实现,默认为小于比较,中序遍历得到升序序列),内存池。

一般来说,后两个参数有缺省值,的使用频率较低,我们在日常使用的过程中只需要传入第一个参数即可。

3.1 常用接口

STL的容器都是大同小异,这里只列出需要注意的。

3.1.1 构造函数
set();默认构造
template <class InputIterator>
set(InputIterator first, InputIterator last);
迭代器区间构造
set(const set& x);拷贝构造

在C++11之后还支持用列表构造:

// initializer list (5) initializer 列表构造
set (initializer_list<value_type> il,
     const key_compare& comp = key_compare(),
     const allocator_type& alloc = allocator_type());


eg:
set<int> mySet1({1, 2, 3, 4});
set<int> mySet2 = {1, 2, 3, 4};

在原文档中,前两个构造函数的参数列表还包括比较器和内存池:

但是经过我自己的测试发现,如果在定义set的时候传入比较器或内存池,这个定义语句会被编译器识别成函数的声明:

也就是说,比较器和内存池依然只能在类型参数列表中传入。

 3.1.2 迭代器

这里就不列表了,set的迭代器是双向迭代器,函数还是那几个函数。

正向迭代器的是按照key值从小到大进行遍历。

除此之外,在set中无论是iterator还是const_iterator都不支持通过解引用修改set中的数据,因为这样会破坏红黑树的底层结构。

3.1.3 增删
(1)pair<iterator, bool> insert(const value_type& val);
(2)iterator insert(iterator position, const value_type& val);
(3)template <class InputIterator>
   void insert(InputIterator first, InputIterator last);

(1)按值插入

(2)指定位置插入

(3)将容器的迭代器区间内的值插入

(1)void erase(iterator position);
(2)size_type erase(const value_type& val);
(3)void erase(iterator first, iterator last);

(1)删除指定位置的值

(2)删除指定的值

(3)删除一个迭代器区间

insert函数(1)的返回值是一个pair:

插入成功时:iterator为新插入数据的迭代器,bool为true

插入失败时:iterator为已有的相同值的迭代器,bool为false

 insert函数(2)的第一个参数实质上只是一个插入建议,由于set的特殊的红黑树的底层结构,该插入建议可能不会被采纳。

 3.1.4 查找
iterator find (const value_type& val) const;查找某个值
size_type count (const value_type& val) const;查找某个值在set中有几个

set不支持相同的值进行插入,所以count的结果不是1就是0。

这样看来count似乎有点鸡肋,或者说与名称不符,但set中的count实际上是为了与multiset统一而存在的。

但count也并非一无是处,如果只想确定某个值存不存在的话,count就稍微比find方便一点:

if(mySet.find(24) != mySet.end())
{
    // ......
}

if(mySet.count(24))
{
    // ......
}

3.2 multiset

基本与set相同,但是支持插入相同的数据,由此而引发的变化还有:

1. insert一定成功,插入函数(1)的返回值不再是pair而只有iterator。

2. count返回数据在set中的个数。

3. 如果有多个相同的值,无论是删除还是查找,都是对中序遍历的第一个进行操作。

 4. map的介绍

参考文档:map - C++ Reference,关于map的使用,将其当作key/value搜索场景的红黑树来使用即可。

map的原型:

template < class Key,                                     // map::key_type
           class T,                                       // map::mapped_type
           class Compare = less<Key>,                     // map::key_compare
           class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
           > class map;

类型参数分别为:key的类型,value的类型,比较器(通过仿函数实现,默认为小于比较,中序遍历得到升序序列),内存池。

除此之外,pair在map中起到了将key和value绑定到一起的作用,所以在map的结点中存的数据实际上是pair,而没有单独存储key和value。

typedef pair<const Key, T> value_type;

同样,大多数情况下只需要传入前两个参数即可。

4.1 常用接口

同样,此处只列出较为重要或与其他容器有所不同的。

4.1.1 构造函数
map();默认构造
template <class InputIterator>
map (InputIterator first, InputIterator last);
迭代器区间构造
map (const map& x);拷贝构造

在C++11之后支持列表构造:

// initializer list (5) initializer 列表构造
map (initializer_list<value_type> il,
     const key_compare& comp = key_compare(),
     const allocator_type& alloc = allocator_type());

eg:
map<string, int> myMap1({{"张三", 18}, {"李四", 21}, {"王五", 35}});
map<string, int> myMap2 = {{"张三", 18}, {"李四", 21}, {"王五", 35}};

内层大括号会通过pair的列表构造隐式类型转换为pair<string, int>类型。 

关于参考文档中给出的函数原型,依然存在着和set相同的问题。

 4.1.2 迭代器

函数还是那些函数,迭代器还是双向迭代器。

但与set不同的是,map的迭代器支持对value进行修改,而依然不支持对key进行修改。

由于map结点中存的是pair<key, value>,所以按照逻辑来说,map的迭代器解引用得到的就是pair<key, value>。

在pair中,key对应的是first,value对应的是second:

map<string, int> myMap(...);
map<string, int>::iterator it = myMap.begin();
while(it != myMap.end())
{
    cout << "key:" << (*it).first << " value:" << *(it).second << endl;
    cout << "key:" << it->first << " value:" << it->second << endl;
}
4.1.3 增删
(1)pair<iterator, bool> insert(const value_type& val);
(2)iterator insert(iterator position, const value_type& val);
(3)template <class InputIterator>
   void insert(InputIterator first, InputIterator last);

(1)按值插入

(2)指定位置插入

(3)将容器的迭代器区间内的值插入

(1)void erase(iterator position);
(2)size_type erase(const key_type& k);
(3)void erase(iterator first, iterator last);

(1)删除指定位置的值

(2)删除指定的值

(3)删除一个迭代器区间

函数原型与set完全一样,除了erase函数(2),只需要key即可找到对应的数据进行删除。

但是要切记,这里的value_type是pair。

insert函数(1)的返回值逻辑也与set相同,但是只要key相同就不支持继续插入了。

4.1.4 查找
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;
按键值key查找
size_type count (const key_type& k) const;查找某个键值key在set中有几个

 同样这里的count函数的返回值也只能是1或0。

4.1.5 operator[]

没错,map重载了"[]"。

但是,按理来说,只有支持随机迭代器的容器才可以重载"[]",而map不是双向迭代器吗?

看了函数原型你就明白了:

mapped_type& operator[] (const key_type& k);

所以,这个"[]"的功能是根据key来返回value的引用,而与迭代器没有关系。

但这个函数并不简单:

当key存在时,返回其对应的value的引用。

当key不存在时,在map中插入key,并给value默认值,然后返回value的引用。

所以map中的"[]"可以说是"查找,修改,插入"三位一体。

这是由于operator[]的内部实现是基于insert函数完成的:

// operator[]的内部实现
mapped_type& operator[] (const key_type& k)
{
    // 1、如果k不在map中,insert会插⼊k和mapped_type默认值,同时[]返回结点中存储
    //mapped_type值的引⽤,那么我们可以通过引⽤修改返映射值。所以[]具备了插⼊+修改功能

    // 2、如果k在map中,insert会插⼊失败,但是insert返回pair对象的first是指向key结点的
    //迭代器,返回值同时[]返回结点中存储mapped_type值的引⽤,所以[]具备了查找+修改的功能

    pair<iterator, bool> ret = insert({ k, mapped_type() });
    iterator it = ret.first;
    return it->second;
}

 4.2 multimap

基本与map相同,但是支持插入相同的数据,由此而引发的变化还有:

1. insert一定成功,插入函数(1)的返回值不再是pair而只有iterator。

2. count返回数据在map中的个数。

3. 如果有多个相同的值,无论是删除还是查找,都是对中序遍历的第一个进行操作。

4. 不再支持operator[],因为同一个key存在多组值。

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

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

相关文章

数据驱动农业——农业中的大数据

橙蜂智能公司致力于提供先进的人工智能和物联网解决方案&#xff0c;帮助企业优化运营并实现技术潜能。公司主要服务包括AI数字人、AI翻译、埃域知识库、大模型服务等。其核心价值观为创新、客户至上、质量、合作和可持续发展。 橙蜂智农的智慧农业产品涵盖了多方面的功能&…

win操作系统各个版本下载

MSDN, 我告诉你 - 做一个安静的工具站 (itellyou.cn) 下载地址链接转换工具 - 在线工具 (tool.lu) 通过这个网站可以再将ed2k转换成别的下载工具链接

LLM安全风险及应对

LLM安全风险主要从四个维度分析&#xff1a;用户输入、训练数据、模型本身以及工具和插件。 风险类别具体风险风险解释应对措施具体举例用户输入相关风险提示注入&#xff08;Prompt Injection&#xff09;攻击者通过设计特定输入&#xff0c;使模型生成恶意或不安全的输出。- …

进度调度切换

tips&#xff1a; 进程&#xff1d;内核数据结构&#xff08;PCB/task_struct&#xff09;&#xff0b;代码和数据 UID&#xff1a;每个用户对应的id 进程分类 & 进程关系 僵尸进程 父在子死 进程退出 代码不会再执行了&#xff0c;系统可以立即释放对应代码和数据进程…

推荐一款开源的Redis桌面客户端

TinyRDM 是一个现代化的、轻量级的跨平台 Redis 桌面客户端&#xff0c;能在 Mac、Windows 和 Linux 系统上使用。它有着现代化的设计风格&#xff0c;界面既简洁又清晰&#xff0c;操作起来方便又高效。不管是刚开始接触的新手&#xff0c;还是经验丰富的开发者&#xff0c;都…

【安装教程】Windows环境下Neo4j的安装与配置

【安装教程】Windows环境下Neo4j的安装与配置 Neo4j的概念一、安装前准备——JDK二、Neo4j的安装三、Neo4j的环境配置四、安装验证 Neo4j的概念 Neo4j 是一个高性能的图形数据库管理系统&#xff0c;它使用图形模型来存储和处理数据。Neo4j 的图形模型由节点和边组成&#xff0…

WebLogic 漏洞复现

1、后台弱⼝令GetShell 默认账号密码&#xff1a;weblogic/Oracle123 weblogic常⽤弱⼝令&#xff1a;https://cirt.net/passwords?criteriaweblogic 这⾥注意&#xff0c; 单个账号错误密码5次之后就会⾃动锁定。 http://47.121.212.195:7001/console 2、登录后台后&#…

矩阵分析 线性空间和线性变换 笔记手稿

1线性空间的概念 数域 线性空间 证明 线性相关和线性无关 线性空间的基 基的性质 基变换和坐标变换 子空间和维数定理 两个子空间的交和和都是子空间 维数公式 子空间扩充定理 维数公式及其证明 直和 证明 线性空间的同构 注意&#xff1a;映射的概念 同构的性质 线性变换的…

前端组件库Element UI 的使用

一、准备工作 1.确保安装了开发软件 VS Code&#xff08;此处可查阅安装 VS Code教程&#xff09;&#xff0c;确保相关插件安装成功 2.安装Node.js 和创建Vue项目&#xff08;此处可查阅安装创建教程&#xff09; 3.成功在VS Code运行一个Vue项目&#xff08;此处可查阅运行…

HTML+CSS学习笔记

目录 HTML 1.开发环境 2.创建HTML文件 3.HTML元素 3.1HTML文件结构 3.2HTML标签 3.3HTML属性​编辑​编辑 3.4HTML区块 3.4.1块元素 3.4.2行内元素 3.5HTML表单 CSS 1.CSS简介 2.CSS语法​编辑 3.CSS三种导入方式 内联样式 内部样式 外部样式 4.选择器​ 5.C…

老挝语方言那么多,怎么沟通交流?可以用《老挝语翻译通》app

准备前往老挝探险&#xff0c;却担心语言不通&#xff1f;《老挝语翻译通》App来帮忙&#xff0c;专为老挝语学习者和旅行者设计&#xff0c;让你轻松掌握老挝语&#xff0c;无需打字&#xff0c;说话即可翻译。 应用特色&#xff1a; 中老互译&#xff1a;实时中文与老挝语互…

xlsx单元格宽度自适应内容宽度

xlsx单元格宽度自适应内容宽度 全选内容区域。参考 按下图操作

文心智能体搭建步骤

通过使用文心智能体平台来创建智能体的过程。这种方法可以让没有编程经验的人也能快速构建智能体&#xff0c;降低了技 术门槛。以下是一些建议和心得: 1.选择合适的平台:文心智能体平台是一个优秀的选择&#xff0c;它提供了零代码和低代码的开发环境&#xff0c;极大地降低了…

[Linux][进程] 认识进程

基本概念 进程是一个操作系统术语,用来管理与操作程序.在windows下打开任务管理器即可查看目前打开的所有进程 PCB 进程控制块,从代码层面来说 PCB 是进程所有属性的一个结构体,在Linux源码中PCB指的是struct task_struct. Linux环境下: 进程 task_struct 代码 …

科技云报到:以数据“价值三角”为擎,探索数据治理实践路径

科技云报到原创。 过去四十年&#xff0c;经济发展主要来自于土地、劳动力、农业技术、工业技术等要素的充分释放。面向数字经济时代&#xff0c;无论是大模型、自动驾驶还是具身智能、人形机器人&#xff0c;数据已然成为继土地、劳动、资本和技术之后的又一种战略资产和新型…

【遥感图像船舶检测数据集】

【遥感图像船舶检测数据集】nc1 标签names: [ship,] 名称&#xff1a;【‘船’】共4126张&#xff0c;8:1:1比例划分&#xff0c;&#xff08;train;3300张&#xff0c;val&#xff1a;412张&#xff0c;test&#xff1a;414张标注文件为YOLO适用的txt格式。可以直接用于模型训…

数字化转型加速,报表工具助力制造业变革

在当前全球制造业加速迈向数字化的背景下&#xff0c;企业正面临前所未有的挑战和机遇。然而&#xff0c;制造业的数字化转型并非一蹴而就&#xff0c;许多企业在推进过程中遇到了各种痛点。 制造业数字化转型的痛点 制造业的生产流程复杂&#xff0c;涉及多种设备、工艺和原…

每日一练:翻转二叉树

226. 翻转二叉树 - 力扣&#xff08;LeetCode&#xff09; 一、题目要求 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&#xff…

新160个crackme - 063-dc0de-crackme

运行分析 需要破解Username和Serial PE分析 Delphi程序&#xff0c;32位&#xff0c;无壳 静态分析&动态调试 ida搜索到关键字符串&#xff0c;双击进入函数 进行动态调制函数较长&#xff0c;共有5个循环&#xff0c;以循环为单位逐步分析&#xff0c;注释如上循环1&#…