C++漫溯键值的长河:map set

文章目录

  • 1.关联式容器
  • 2.set
    • 2.1 find
    • 2.2 lower_bound、upper_bound
  • 3.multiset
    • 3.1 count
    • 3.2 equal_range
  • 4.map
    • 4.1 insert
    • 4.2 operate->
    • 4.3 operate[ ]
    • 4.4 map的应用实践:随机链表的复制
  • 5.multimap
  • 希望读者们多多三连支持
  • 小编会继续更新
  • 你们的鼓励就是我前进的动力!

迄今为止,除了二叉搜索树以外的结构,我们学习到的顺序表,链表,栈和队列等都属于这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身

1.关联式容器

根据应用场景的不同,STL 总共实现了两种不同结构的管理式容器:树型结构哈希结构。树型结构的关联式容器主要有四种:mapsetmultimapmultiset。这四种容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列

关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高

键对值中的 key 表示键值,value 表示与 key 对应的信息

SGI-STL中关于键值对的定义:

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){}
};

2.set

在这里插入图片描述

set 的主要特征可总结为:

  1. set 是按照一定次序存储元素的容器
  2. set 中,元素的 value 也标识它( value 就是 key,类型为 T ),并且每个 value 必须是唯一的 set 中的元素不能在容器中修改(元素总是 const ),但是可以从容器中插入或删除它们
  3. 在内部,set 中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序
  4. set 容器通过 key 访问单个元素的速度通常比 unordered_set 容器慢,但它们允许根据顺序对子集进行直接迭代
  5. set 在底层是用二叉搜索树(红黑树)实现的

🔥值得注意的是:

  1. map/multimap 不同,map/multimap 中存储的是真正的键值对 <key, value>set 中只放 value,但在底层实际存放的是由 <value, value> 构成的键值对(后面底层的博客会解释)
  2. set 中插入元素时,只需要插入 value 即可,不需要构造键值对
  3. set 中的元素不可以重复(因此可以使用 set 进行去重)。
  4. 使用 set 的迭代器遍历 set 中的元素,可以得到有序序列
  5. set 中的元素默认按照小于来比较,即 123…的顺序
  6. set 中查找某个元素,时间复杂度为: l o g 2 n log_2 n log2n
  7. set 中的元素不允许修改
  8. set 中的底层使用二叉搜索树(红黑树)来实现

2.1 find

在这里插入图片描述

由于 set 的基本功能,像 inserterase、迭代器等都和 stringvector 等差不多,这里就不过多解释,详细的可以自行查看官方文档,本文将针对部分特殊的函数进行解析

find 简单来说,就是寻找特定的键值,那么可以提出一个问题:

set<int> s;
s.insert(3);
s.insert(2);
s.insert(4);
s.insert(5);
s.insert(1);
s.insert(5);
s.insert(2)
s.insert(5);auto pos = s.find(3);//第一种
auto pos = find(s.begin(), s.end(), 3);//第二种
s.erase(3);

哪一种 find 方式能更好的删除?显然是第一种

因为第一种是 set 里面的 find,会以平衡二叉搜索树的方式去查找,大的往左走,小的往右走,时间复杂度为 O(logN);第二种是 algorithm(算法头文件)中的 find,是以依次遍历的方式,即中序遍历的方式进行的,时间复杂度为 O(N)

2.2 lower_bound、upper_bound

set<int> myset;
set<int>::iterator itlow, itup;for (int i = 1; i < 10; i++) myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90itlow = myset.lower_bound(30);                //            ^
itup = myset.upper_bound(65);                 //                        ^myset.erase(itlow, itup);                     // 10 20 70 80 90cout << "myset contains:";
for (set<int>::iterator it = myset.begin(); it != myset.end(); ++it)cout << ' ' << *it;
cout << '\n';

因为迭代器的区间遵循左闭右开原则,所以 lower_bound 用于查找第一个大于等于给定值 val 的元素位置,upper_bound 用于查找第一个大于给定值 val 的元素位置

3.multiset

在这里插入图片描述
multiset 的主要特征可总结为:

  1. multiset 是按照特定顺序存储元素的容器,其中元素是可以重复的
  2. multiset 中,元素的 value 也会识别它(因为 multiset 中本身存储的就是 <value, value> 组成的键值对,因此 value本身就是 keykey就是 value,类型为 T ),multiset 元素的值不能在容器中进行修改(因为元素总是 const 的),但可以从容器中插入或删除
  3. 在内部,multiset 中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序
  4. multiset 容器通过 key 访问单个元素的速度通常比 unordered_multiset 容器慢,但当使用迭代器遍历时会得到一个有序序列
  5. multiset 底层结构为二叉搜索树(红黑树)

🔥值得注意的是:

  1. multiset 中再底层中存储的是 <value, value> 的键值对
  2. multiset 的插入接口中只需要插入即可
  3. set 的区别是,multiset 中的元素可以重复,set 是中 value 是唯一的
  4. 使用迭代器对 multiset 中的元素进行遍历,可以得到有序的序列
  5. multiset 中的元素不能修改
  6. multiset 中找某个元素,时间复杂度为 O ( l o g 2 N ) O(log_2 N) O(log2N)
  7. multiset 的作用:可以对元素进行排序

3.1 count

在这里插入图片描述

multiset 同样是这几个,但是 countequal_range 可以说是专门给 multiset 打造的,虽然 set 里也可以用,但是没什么意义

count 用于统计容器中某个值出现的次数

3.2 equal_range

set<int> mySet = {1, 2, 3, 3, 4, 5};
auto result = mySet.equal_range(3);for (auto it = result.first; it != result.second; ++it) 
{cout << *it << " ";
}
cout << endl;

equal_range 用于查找重复元素之间的区间,返回一个 pair 对象,该对象包含两个迭代器:

  1. 第一个迭代器指向 multiset 中第一个等于 value 的元素(如果存在),或者指向第一个大于 value 的元素(如果不存在等于 value 的元素)
  2. 第二个迭代器指向 set 中最后一个等于 value 的元素的下一个位置(如果存在等于 value 的元素),或者与第一个迭代器相等(如果不存在等于 value 的元素)

4.map

在这里插入图片描述

map的主要特征可总结为:

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

由于 map 的基本功能,像 inserterase、迭代器等都和 stringvector 等差不多,这里就不过多解释,详细的可以自行查看官方文档,本文将针对部分函数进行解析

4.1 insert

map 中的 insert 插入的是一个 pair 结构对象,下面将列举多种插入方式:

🚩创建普通对象插入

pair<string, string> kv1("insert", "插入");
dict.insert(kv1);

🚩创建匿名对象插入

dict.insert(pair<string, string>("sort", "排序"));

🚩调用make_pair函数插入

dict.insert(make_pair("string", "字符串"));

调用 make_pair 省去了声明类型的过程

🚩隐式类型转换插入

dict.insert({ "string","字符串" });

通常 C++98 只支持单参数隐式类型转换,到 C++11 的时候就开始支持多参数隐式类型转换

有这么一个问题:为什么加上了引用反而要加const

pair<string, string> kv2 = { "insert", "插入" };
const pair<string, string>& kv2 = { "insert", "插入" };

无引用情况: 对于 pair<string, string> kv2 = { "string", "字符串" }; ,编译器可能会执行拷贝省略(也叫返回值优化 RVO 或命名返回值优化 NRVO )。比如在创建 kv2 时,直接在其存储位置构造对象,而不是先创建一个临时对象再拷贝 / 移动过去

加引用情况: 使用 const pair<string, string>& kv2 = { "string", "字符串" }; 时,这里 kv2 是引用,它绑定到一个临时对象(由大括号初始化列表创建 )。因为引用本身不持有对象,只是给对象取别名,所以不存在像非引用对象构造时那种在自身存储位置直接构造的情况。不过,这种引用绑定临时对象的方式,只要临时对象的生命周期延长到与引用一样长(C++ 规则规定,常量左值引用绑定临时对象时,临时对象生命周期延长 ),也不会额外增加拷贝 / 移动开销

4.2 operate->

map<string, string>::iterator it = dict.begin();
while (it != dict.end())
{//it->first = "xxx";//it->second = "xxx";//cout << (*it).first << ":" << (*it).second << endl;cout << it->first << ":" << it->second << endl;++it;
}
cout << endl;

map 中并没有对 pair 进行流插入运算符重载,(*it).first 这样子的方式又不太简洁不好看,所以进行了 -> 运算符重载,返回的是 first 的地址,因此 (*it).first 等价于 it->->first,为了代码可读性,就省略一个 ->

4.3 operate[ ]

在这里插入图片描述

map 中提供了 [] 运算符重载,可以通过 key 来访问 value

在这里插入图片描述

首先我们知道 insert 的返回值 key 的部分是一个迭代器,value 的部分是个布尔值,文档中对该返回值的解释是:

  1. key 已经在树里面,返回 pair<树里面key所在节点的iterator,false>false 表示不用插入了
  2. key 不在树里面,返回 pair<树里面key所在节点的iterator,true>true 表示需要插入新节点

在这里插入图片描述

再来看,左边是官方文档的原始定义,那么转化成右边的定义能够更直观理解其底层

这里 V 代表值类型,K 代表键类型 。operator[] 是操作符重载函数,接受一个常量引用类型的键 key ,返回值类型 V 的引用。这样设计是为了支持对容器内元素的读写操作。例如,可以通过 map[key] = newValue; 来修改值,或者通过 auto value = map[key]; 来读取值

然后通过 insert 判断是否插入新节点,最后返回指定节点的 value

4.4 map的应用实践:随机链表的复制

✏️题目描述:

在这里插入图片描述

✏️示例:

在这里插入图片描述

传送门: 随机链表的复制

题解:

利用 map 的映射机制,首先,在第一次遍历原链表时,为原链表的每个节点创建一个对应的新节点,并将原节点和新节点的映射关系存储在 map 中。然后,在第二次遍历原链表时,对于原链表中的每个节点 cur,我们可以通过 cur->random 找到其随机指针指向的原节点,再利用之前存储的映射关系,在 map 中查找该原节点对应的新节点,将这个新节点赋值给当前新节点 copynode 的随机指针 copynode->random

🔥值得注意的是:

记录的不是cur和newnode的关系吗,为什么可以通过cur->random找到newnode->random?

假设原链表有三个节点 ABC,节点 A 的随机指针指向节点 C
建立映射阶段: 会为 ABC 分别创建对应的新节点 A'B'C',并在 nodeCopyMap 中记录映射关系:{A -> A', B -> B', C -> C'}。
设置随机指针阶段: 当处理节点 A 时,cur 指向 Acur->random 指向 C。由于 C 作为键存在于 nodeCopyMap 中,通过 nodeCopyMap[cur->random] 也就是 nodeCopyMap[C] 可以找到 C',接着把 C' 赋值给 A' 的随机指针 A'->random,这样新链表中节点 A' 的随机指针就正确地指向了节点 C',和原链表中节点 A 的随机指针指向 C 相对应

💻代码实现:

class Solution {
public:Node* copyRandomList(Node* head) {map<Node*, Node*> nodeCopyMap;Node* copyhead = nullptr;Node* copytail = nullptr;Node* cur = head;while (cur){Node* copynode = new Node(cur->val);if (copytail == nullptr){copyhead = copytail = copynode;}else{copytail->next = copynode;copytail = copynode;}nodeCopyMap[cur] = copynode;cur = cur->next;}Node* copy = copyhead;cur = head;while (cur){if (cur->random == nullptr){copy->random = nullptr;}else{copy->random = nodeCopyMap[cur->random];}cur = cur->next;copy = copy->next;}return copyhead;}
};

5.multimap

在这里插入图片描述

multimap的主要特征可总结为:

  1. multimaps 是关联式容器,它按照特定的顺序,存储由 keyvalue 映射成的键值对 <key, value>,其中多个键值对之间的 key 是可以重复的。
  2. multimap 中,通常按照 key 排序和惟一地标识元素,而映射的 value 存储与 key 关联的内容。keyvalue 的类型可能不同,通过 multimap 内部的成员类型 value_type 组合在一起,value_type 是组合 keyvalue 的键值对: typedef pair<const Key, T> value_type;
  3. 在内部,multimap 中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对 key 进行排序的。
  4. multimap 通过 key 访问单个元素的速度通常比 unordered_multimap 容器慢,但是使用迭代器直接遍历 multimap 中的元素可以得到关于 key 有序的序列
  5. multimap 在底层用二叉搜索树(红黑树)来实现

注意:multimapmap 的唯一不同就是:map 中的 key 是唯一的,而 multimapkey 是可以重复的

🔥值得注意的是:

  1. multimap 中的 key 是可以重复的
  2. multimap 中的元素默认将 key 按照小于来比较
  3. multimap 中没有重载 operator[] 操作,因为一个 key 对应多个 value ,不知道找哪个 value
  4. 使用时与 map 包含的头文件相同

multimapmutiset 是差不多的,而且在实际应用中用的不多,所以这里就不细讲了


希望读者们多多三连支持

小编会继续更新

你们的鼓励就是我前进的动力!

请添加图片描述

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

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

相关文章

汽车用品商城小程序源码介绍

基于ThinkPHPFastAdminUniApp开发的汽车用品商城小程序源码&#xff0c;从技术架构来看&#xff0c;ThinkPHP作为后端框架&#xff0c;提供了稳定且高效的开发基础&#xff0c;能够处理复杂的业务逻辑和数据交互。FastAdmin则进一步简化了后台管理系统的开发流程&#xff0c;提…

力扣hot100——114.二叉树展开为链表

基于 Morris 遍历思想 将左子树插到右子树的位置&#xff0c;将原来的右子树插到左子树的最右结点&#xff0c;遍历右结点重复以上步骤&#xff0c;直至右结点为空。 class Solution { public:void flatten(TreeNode* root) {if(rootnullptr) return;while(root){if(!root-&g…

JConsole监控centos服务器中的springboot的服务

场景 在centos服务器中,有一个aa.jar的springboot服务,我想用JConsole监控它的JVM情况,具体怎么实现。 配置 Spring Boot 应用以启用 JMX 在java应用启动项进行配置 java -Djava.rmi.server.hostname=服务器IP -Dcom.sun.management.jmxremote=true \ -Dcom.sun.managem…

39.RocketMQ高性能核心原理与源码架构剖析

1. 源码环境搭建 1.1 主要功能模块 ​ RocketMQ的官方Git仓库地址&#xff1a;GitHub - apache/rocketmq: Apache RocketMQ is a cloud native messaging and streaming platform, making it simple to build event-driven applications. ​ RocketMQ的官方网站上下载指定版…

施磊老师rpc(一)

文章目录 mprpc项目**项目概述**&#xff1a;深入学习到什么**前置学习建议**&#xff1a;核心内容其他技术与工具**项目特点与要求**&#xff1a;**环境准备**&#xff1a; 技术栈集群和分布式理论单机聊天服务器案例分析集群聊天服务器分析分布式系统介绍多个模块的局限引入分…

基于LangChain构建最小智能体(Agent)实现指南

摘要 本文完整解析基于LangChain的极简Agent实现方案&#xff0c;通过26行代码构建具备网络搜索能力的对话系统&#xff0c;涵盖Agent初始化、工具集成、流式回调等核心技术要点。适用于LLM应用开发者快速入门Agent开发。(参考项目代码&#xff1a;Minimal Agent) 系统架构设计…

AWTK:一键切换皮肤,打造个性化UI

想让你的应用在不同场景下都能完美呈现吗&#xff1f;皮肤切换功能必不可少&#xff01;本文将介绍AWTK&#xff0c;一款强大的GUI框架&#xff0c;它通过内置资源管理和优化缓存&#xff0c;轻松实现皮肤切换功能。 前言 当今的UI应用中&#xff0c;为了满足不同使用场景和…

【Vagrant+VirtualBox创建自动化虚拟环境】Ansible测试Playbook

文章目录 Vagrant安装vagrant安装 VirtualBox如何使用 Ansible安装AnsiblePlaybook测试创建hosts文件创建setup.yml文件 Vagrant Vagrant是一个基于Ruby的工具&#xff0c;用于创建和部署虚拟化开发环境。它使用Oracle的开源VirtualBox虚拟化系统&#xff0c;使用 Chef创建自动…

AI在医疗领域的10大应用:从疾病预测到手术机器人

AI在医疗领域的10大应用&#xff1a;从疾病预测到手术机器人 系统化学习人工智能网站&#xff08;收藏&#xff09;&#xff1a;https://www.captainbed.cn/flu 文章目录 AI在医疗领域的10大应用&#xff1a;从疾病预测到手术机器人摘要引言1. 医学影像诊断&#xff1a;从静态…

Win11 配置 Git 绑定 Github 账号的方法与问题汇总

目录 一、创建 Github 项目库&#xff08;远程仓库&#xff09;二、配置安装好的 Git1. 设置用户信息2. 查看已配置的信息3. 建立本地仓库4. Git 的常用命令1&#xff09;git checkout&#xff08;切换&#xff09;2&#xff09;git push&#xff08;上传&#xff09;3&#xf…

6.应用层

6. 应用层 1. 概述 应用层是计算机网络体系结构的最顶层&#xff0c;是设计和建立计算机网络的最终目的&#xff0c;也是计算机网络中发展最快的部分 早期基于文本的应用&#xff08;电子邮件、远程登录、文件传输、新闻组&#xff09;20世纪90年代将因特网带入千家万户的万维…

FPGA 100G UDP纯逻辑协议栈

随着器件等级的升高&#xff0c;高速serdes的线速率也随之提高&#xff0c;RFSOC 4x最大可支持100G&#xff0c;主流方案为RDMA方案&#xff0c;该方案相对比较复杂&#xff0c;除了需要负责逻辑端的开发&#xff0c;还需操作系统中开发RDMA的驱动&#xff0c;对于对丢包不那么…

CSS实现DIV水平与垂直居中方法总结

大家好&#xff0c;欢迎来到程序视点&#xff01;我是你们的老朋友.小二&#xff01; CSS实现DIV水平与垂直居中方法总结 一、水平居中方案 标准方法 .center-div {margin-left: auto;margin-right: auto; }关键点&#xff1a;必须声明DOCTYPE&#xff08;推荐XHTML 1.0 Tran…

Qt快速上手:QSettings高效配置读写实战指南

文章目录 前言一、QSettings初识&#xff1a;配置管理利器二、基础操作三板斧2.1 文件读写基础2.2 数据类型处理指南2.3 分组管理技巧 三、高级技巧&#xff1a;精准控制配置项3.1 监听配置变更3.2 批量操作配置项 四、避坑指南&#xff1a;那些你可能会遇到的问题4.1 键顺序重…

2025运维工程师面试题1(答案在后一张)

一、逻辑思维能力考核&#xff1a; 问题1&#xff1a; 3个人去投宿&#xff0c;一晚30元三个人每人掏了10元凑够30元交给了老板后来老板说今天优惠只要25元就够了&#xff0c;拿出5元命令服务生退还给他们&#xff0c;服务生偷偷藏起了2元&#xff0c;然后&#xff0c;把剩下…

react中封装一个预览.doc和.docx文件的组件

主要用到了mammoth这个插件,mammoth.js‌是一个JavaScript库&#xff0c;主要用于将Microsoft Word文档&#xff08;.docx格式&#xff09;转换为HTML。它可以通过Node.js环境使用&#xff0c;也可以直接在浏览器中使用。 关键代码: import mammoth from mammoth; import { u…

c#WebsocketSever

这是一个winFrom的小工具&#xff0c;用于再本机创建一个c#服务的项目。 1、将本机ip地址改为左上角Ip&#xff0c;注意没有“&#xff1a;”后的部分&#xff0c;那是端口号。 2、点击中间按钮&#xff0c;启动服务器 3、如果启动成功&#xff0c;会在下面显示启动成功&…

顶会招牌idea:机器学习+组合优化 优秀论文合集

2025深度学习发论文&模型涨点之——机器学习组合优化 机器学习&#xff08;ML&#xff09;与组合优化&#xff08;CO&#xff09;的交叉研究已成为运筹学与人工智能领域的前沿方向。传统组合优化方法&#xff08;如分支定界、动态规划&#xff09;虽在理论上有严格的性能保…

服务器硬件老化导致性能下降的排查与优化

随着企业数字化转型的深入&#xff0c;服务器作为IT基础设施的核心载体&#xff0c;其稳定性与性能直接影响业务连续性。然而&#xff0c;硬件老化导致的性能衰减问题普遍存在且易被忽视。本报告通过系统性分析服务器硬件老化现象&#xff0c;提出多维度排查方法与优化方案&…

删除k8s某命名空间,一直卡住了怎么办?

以 kubectl delete ns cert-manager 命令卡住为例&#xff0c;并且命名空间一直处于 Terminating 状态&#xff0c;说明 Kubernetes 无法完成删除操作&#xff0c;通常是因为 Finalizers 阻塞或某些资源无法正常清理。 解决方法 1. 检查命名空间状态 kubectl get ns cert-man…