map和set 的封装
文章目录
- 引入
- key-value模型
- map和set底层
- set
- set的几个重要接口
- map
- map几个重要的接口
- map和set的封装
引入
对于map和set的引入,我们用一道在程序中常见的问题解决:
给定一个数组int arr[]={1,2,1,3,1,4,1,5,5,2,3,4,5};
,给出以下问题的解决方案:
- 找出出现次数最多的元素
- 去除数组中重复的元素,并输出(任意顺序)
这些问题在没有学习map和set之前并没有很好的解决方法(但是map和set并不是唯一的解决方法),第二个问题:之前我们解决的方法一般就开辟一个新数组tmp,遍历arr中所有元素,对于arr中任意元素遍历tmp数组如果元素在tmp中未出现就插入。第一个问题,只需将数组类型改成一个结构体,该结构体包含一个元素和他出现的次数,则在最后一步如果arr中元素已经出现在tmp中,就改变tmp中该元素对应结构体的次数。
我们发现arr在tmp中插入时每一次都要遍历一遍tmp数组,是否能提升查找效率呢?
所以我们现在需要一个容器:
- 该容器中不存在重复元素
- 该容器中查找一个元素效率要高
key-value模型
key-value模型适用于上面的问题1,每一个元素(key)和他出现的次数(value)一 一对应,但是每次查找tmp数组,都是以key为依据查找。
举个例子:一个英汉字典就是典型的key-value模型,英文(key)是你搜索的依据,中文(value)是查找的内容,两者一一对应
map和set底层
有一个数据结构完美符合上面我们需要容器的两个条件——红黑树。首先红黑树不能插入重复元素,所以可以做到去重的效果。又因为满足二叉搜索树所以查找效率比高。而且比二叉搜索树稳定。所以map和set的底层采用的是红黑树
set
set存储元素的类型为key
,容器中将该类型定义为value_type
set的几个重要接口
insert
注意insert的返回值是一个pair类型,first是插入元素的迭代器(如果val在set中不存在返回插入新元素的迭代器,如果在set中存在返回set中值为val的那个元素的迭代器),second代表是否插入成功pair<iterator,bool> insert (const value_type& val);pair<iterator,bool> insert (value_type&& val);
- operator++
由于set底层采用的是红黑树,迭代器++则采用的是树的中序遍历,所以遍历的结果应该是一个有序数组
map
map中存储的元素的类型是:pair<key,value>
,容器中将该类型定义为value_type
map几个重要的接口
insert
返回值pair<iterator,bool> insert (const value_type& val); template <class P> pair<iterator,bool> insert (P&& val);
注意insert的返回值是一个pair类型,first是插入元素的迭代器(如果val在set中不存在返回插入新元素的迭代器,如果在set中存在返回set中值为val的那个元素的迭代器),second代表是否插入成功
find
返回值iterator find (const key_type& k); const_iterator find (const key_type& k) const;
如果找到了返回该元素的迭代器,找不到返回map::end()
operator[]
这里mapped_type& operator[] (const key_type& k); mapped_type& operator[] (key_type&& k);
mapped_type
是我们map中存储元素类型pair<key,value>
中的value
这个运算符重载给我们map的使用带来了巨大的方便,map[key]
和(map.insert(key,value()).first)->second
是等价的。
operator++
由于map底层采用的是红黑树,迭代器++则采用的是树的中序遍历,所以遍历的结果应该是一个有序数组
map和set的封装
我们对于map和set封装始终是在红黑树的基础上进行封装的,红黑树的代码可以参考blog红黑树
红黑树的代码接下来的封装都是在这个代码的基础上进行修改的!
封装的结构为:
这里以map的封装为例: map的封装弄明白了,set就非常简单了
首先需要将红黑树的节点模板改一下:
这里修改后使得树节点里面存储的是ValueType
类型,ValueType
类型实际上就是修改前pair<K,V>
,我们可以发现ValueType
实际上就是我们在外层插入时插入时传进去的类型
template <class ValueType>struct TreeNode{};template <class K, class ValueType, class GetKey>
class RBTree{};
那么问题来了:
- 已经传入了
ValueType
为什么要传入K?
传入Key类型只是作为返回值类型,因为find函数参数为const K& x
,仅此而已。
- 红黑树在插入搜索时需要比较key值,节点里面存储的是ValueType,如何比较?
有些同学会认为很简单,直接(ValueType)x.second
不就行了?这样确实可以,但是没有考虑一个问题set也需要使用这份红黑树代码作为底层,如果用上面的代码,set中的ValueType
和K
是同一个类型,这时就会出现错误。
我们的解决方法是使用仿函数Get_key
来取出ValueType
中的Value,让上层来决定如何取出方法。这就是仿函数的妙处
剩下来只需要将红黑树中函数封装到上层map中就可以了
#include "RedBlackTree.h"
#include <utility>namespace sht
{template <class Key, class Value>class map{struct Get_Key_From_ValueType{Key operator()(const std::pair<Key, Value> &v){return v.first;}};public:typedef std::pair<const Key, Value> ValueType; //注意这里定义的ValueType是实际树节点里面存的内容typedef typename sht::RBTree<Key, ValueType, Get_Key_From_ValueType>::iterator iterator;typedef typename sht::RBTree<Key, ValueType, Get_Key_From_ValueType>::const_iterator const_iterator;std::pair<iterator,bool> insert(const std::pair<Key, Value> x){return tree.insert(x);}Value& operator [](const Key& x){auto ret = tree.insert(std::make_pair(x, Value()));return (ret.first)->second;}const_iterator begin() const // 常量迭代器{return tree.begin();}const_iterator end() const{return tree.end();}iterator begin() // 普通迭代器 : 注意普通迭代器的key也是无法修改的{return tree.begin();}iterator end(){return tree.end();}private:sht::RBTree<Key, ValueType, Get_Key_From_ValueType> tree;};
}
这里map还有一个设计的技巧如果是const对象,那么begin
和end
要返回const迭代器,这里重载了begin和end函数(参考这篇blog)。这个设计很巧妙😋
接下来是一个硬骨头:迭代器的设计
template <class T, class ptr, class ref> //ref是class RBTreeiterator{public:typedef TreeNode<T> Node;typedef RBTreeiterator<T, ptr, ref> Self;RBTreeiterator(Node *x=nullptr){p = x;}//迭代器的运算符重载Self &operator++();Self &operator--();bool operator==(const RBTreeiterator &x)bool operator!=(const RBTreeiterator &x)T operator*()ptr operator->() Node *p;};
然而一个问题出现了,非const对象想要使用const迭代器时就会出现问题:
例如:map<int,int>::const_iterator it=x.begin();
这时就会报错,因为是非const对象,调用的begin是非const成员函数,返回的是普通迭代器,所以报错的原因是缺乏普通迭代器到const迭代器的转化!!
重写一个拷贝构造函数就完美解决了,在迭代器里面的iterator巧妙的构造出了普通迭代器类型,很巧妙的解决了该问题
迭代器的运算符重载中还有一个难点是operator ++
——即按中序取当前节点的下一个节点,这种问题参考LeetCode剑指 Offer II 055. 二叉搜索树迭代器,我这里就不赘述了。
相关文章:

map和set 的封装
文章目录引入key-value模型map和set底层setset的几个重要接口mapmap几个重要的接口map和set的封装引入 对于map和set的引入,我们用一道在程序中常见的问题解决: 给定一个数组int arr[]{1,2,1,3,1,4,1,5,5,2,3,4,5};,给出以下问题的解决方案&…...

Springboot集成kafka(环境搭建+演示)|超级详细,建议收藏
Springboot集成kafka一、前言🔥二、环境说明🔥三、概念🔥四、CentOS7安装kafka🔥1.下载kafka安装包2.下载好后,进行解压六、kafka项目集成🔥1️⃣pom引入2️⃣配置kafka3️⃣一个kafka消息发送端4️⃣定义一…...

Qt 绘制图表 - Qt Charts版
一、前言 自从 Qt 发布以来,给广大跨平台界面研发人员带来了无数的福利。但是Qt自己却一直没有提供自带的图表库,这就使得 QWT、QCustomPlot 等第三方图表库有了巨大的生存空间,为了降低开发成本,大家都涌向了这些第三方库。这种…...

Java学习笔记 --- JavaScript
一、JavaScript介绍 JavaScript语言诞生主要是完成页面的数据验证。因此它运行在客户端,需要运行浏览器来解析执行JavaScript代码。JS是Netcape网景公司的产品,最早取名为LiveScript;为了吸引更多java程序员。更名为 JavaScript JS是弱类型&…...
AP5216 平均电流型LED 降压恒流驱动器
产品描述 AP5216 是一款 PWM工作模式, 高效率、外围简单、内置功率管,适用于5V~100V输入的高精度降压 LED 恒流驱动芯片。输出最大功率可达 9W,最大电流 1.0A。 AP5216 可实现全亮/半亮功能切换,通过MODE 切换:全亮/…...

B站的多个视频教程,怎样生成一个二维码?
商业插画视频教程、电商运营视频教程、在线网课视频、舞蹈视频教程、摄影视频教程、语言学习教程、纪录片视频…所有你发布在哔哩哔哩上的视频,都可以放在一个二维码里面。 任何人只要扫描这个二维码,就能在线观看你的这些视频教程!分享起来…...

深入底层源码的Listener内存马(内存马系列篇三)
写在前面 继前面的FilterServlet内存马技术,这是系列文章的第三篇了,这篇将给大家带来的是Listener内存马技术。 前置 什么是Listener? 监听器 Listener 是一个实现特定接口的 Java 程序,这个程序专门用于监听另一个 Java 对象…...

云端需求助力跑赢周期,金山办公有望借助ChatGPT加速腾飞
与微软在办公领域“搏杀”了三十年的金山办公,或许正在迎来自己的“第二春”。2月25日,金山办公(688111)发布2022年度业绩快报,全年营收38.85亿元人民币(单位下同),同比增加18.44%&a…...

Vulnhub靶场----8、DC-8
文章目录一、环境搭建二、渗透流程三、思路总结一、环境搭建 DC-8下载地址:https://download.vulnhub.com/dc/DC-8.zip kali:192.168.144.148 DC-8:192.168.144.156 二、渗透流程 1、信息收集nmap -T5 -A -p- -sV -sT 192.168.144.156思路&am…...
Makefile 和 Shell 脚本的区别与联系
以下内容转载于博客Makefile 和 shell 脚本的区别与联系,有删改与内容添加。 参考内容:初学Makefile指南 一、什么是 Makefile? Makefile 描述了整个工程的编译、链接规则。当源码文件比较多的时候就不适合通过输入 gcc 命令来编译…...

java25种设计模式之工厂模式
Java设计模式 - 工厂模式 工厂模式是一种创建模式,因为此模式提供了更好的方法来创建对象。 在工厂模式中,我们创建对象而不将创建逻辑暴露给客户端。 例子 在以下部分中,我们将展示如何使用工厂模式创建对象。 由工厂模式创建的对象将是…...

力扣-2020年最后一次登录
大家好,我是空空star,本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目:1890. 2020年最后一次登录二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.…...

[蓝桥杯] 数学与简单DP问题
文章目录 一、简单数学问题习题练习 1、1 买不到的数目 1、1、1 题目描述 1、1、2 题解关键思路与解答 1、2 饮料换购 1、2、1 题目描述 1、2、2 题解关键思路与解答 二、DP问题习题练习 2、1 背包问题 2、1、1 题目描述 2、1、2 题解关键思路与解答 2、2 摘花生 2、2、1 题目…...

浏览器的渲染过程解析
文章目录浏览器渲染进程有哪些?浏览器的渲染过程浏览器渲染进程有哪些? GUI线程:负责渲染浏览器页面,解析html,css,构建DOM树,CSS规则树,渲染树和绘制页面,当界面需要重…...
【C++容器】std::fstream读写文件错误【2023.03.03】
std::fstream使用细节 1.文件不存不支持时打开文件模式不得有ios::in • 如果文件不存在且打开时包括了ios::in模式则打开文件会失败。 fstream m_f;m_f.open("d://123.csv", ios::in | ios::out | ios::binary);//文件不存在则会打开失败• 我这边尝试行得通的做…...

UVM实战--带有寄存器的加法器
一.整体的设计结构图 这里将DUT换成加法器,可以理解为之前UVM加法器加上寄存器,这里总线的功能不做修改,目的看代码的移植那些部分需要修改。 二.各个组件代码详解 2.1 DUT module dut( input clk, input rst_n, input…...

笔记--学习mini3d代码
主要是记录学习mini3d代码时,查的资料; 从github下载的代码: GitHub - skywind3000/mini3d: 3D Software Renderer in 700 Lines !!3D Software Renderer in 700 Lines !! Contribute to skywind3000/mini3d development by creating an a…...

图片服务器
文章目录一、项目简介二、功能及场景三、业务设计四、数据库设计准备图片表准备实体类五、API设计常用功能封装文件上传文件上传获取图片列表接口获取图片内容删除图片接口六、项目优化七、测试自动化测试测试用例一、项目简介 图片服务器:解决项目中插入图片的问题…...

【JAVA程序设计】【C00110】基于SSM(非maven)的车辆维修管理系统
基于SSM(非maven)的车辆维修管理系统项目简介项目获取开发环境项目技术运行截图项目简介 基于ssm框架非maven开发的车辆维修管理系统共分为三个角色:管理员、用户 管理员角色包含以下功能: 查看用户、添加用户、查看车辆信息、故…...
微积分小课堂:用动态的眼光去找问题的最优解(最大值/最小值)【中学里的解题技巧】
文章目录 引言I 最优化问题1.1 不同形式的最优化1.2 用动态的眼光去找问题的最优解引言 把比较数大小的问题,变成了寻找函数变化拐点的问题,将这两个问题等同起来,需要发明一种工具,叫做导数。有了导数这个工具,求最大值问题就变成了解方程的问题。 用变化的眼光找到最优…...

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...

【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...

MLP实战二:MLP 实现图像数字多分类
任务 实战(二):MLP 实现图像多分类 基于 mnist 数据集,建立 mlp 模型,实现 0-9 数字的十分类 task: 1、实现 mnist 数据载入,可视化图形数字; 2、完成数据预处理:图像数据维度转换与…...

使用homeassistant 插件将tasmota 接入到米家
我写一个一个 将本地tasmoat的的设备同通过ha集成到小爱同学的功能,利用了巴法接入小爱的功能,将本地mqtt转发给巴法以实现小爱控制的功能,前提条件。1需要tasmota 设备, 2.在本地搭建了mqtt服务可, 3.搭建了ha 4.在h…...