C++ Map Set的模拟实现

news2024/9/17 7:22:38

C++ Map Set的模拟实现

文章目录

  • 前言
  • 一、Map 和 Set是什么?
    • 1.Set
    • 2.Map
  • 二、困难点
    • 困难一、set和map中值的类型不同
    • 困难二、Map和Set中值不可修改
    • 困难三、红黑树中迭代器的++和--
      • 1.++
      • 2.- -
    • 困难四、map中[ ] 运算符重载的实现
      • 1.修改红黑树以及Map和Set中insert的返回值
        • 1.修改set
        • 2.修改Map
      • 2.[ ]的重载
  • 总结


前言

随着平衡二叉树和红黑树插入(旋转+变色)的实现,我开始进一步学习Map和Set的模拟实现。


一、Map 和 Set是什么?

1.Set

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

2.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中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序
    对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
  5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
  6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))

二、困难点

困难一、set和map中值的类型不同

由于Set只是Key,Map是KV结构。但其底层都是用红黑树去实现的,那么我们该如何去构造这颗红黑树呢?

  1. 我们通过在红黑树中传三个模板参数来实现这个问题。
template<class K,class V,class Value>

这里的K为Map和Set中Key的类型,而V(真实存储值的地方)对于Map来说是一个pair类型,对于Set来说是Key,Value是一个类模板,它通过在类中重写()括号运算符来使红黑树拿到要比较的值。(Set为Key,Map也为Key)

  1. 对于Set来说
	template <class K>
class set
{
public:
	class SetOfVal
	{
	public:
		const K& operator()(const K& data)
		{
			return data;
		}
	};
private:
	RBTree<K,K,SetOfVal> _t;
};

我们通过在SetOfVal类重写括号运算符,实现在红黑树中用K进行比较。

  1. 对于Map来说
template <class K,class V>
class map
{
public:
	class MapOfVal
	{
	public:
		const K& operator()(const pair<const K, V>& data)
		{
			return data.first;
		}
	};
private:
	RBTree< K,pair<const K,V>,MapOfVal> _t ;
};

我们通过MapOfVal类重写括号运算符,实现在红黑树中用V.first进行比较。

困难二、Map和Set中值不可修改

  1. 对于Map来说,其Key值是不可修改的,而Value值是可以修改的。
    这里我们借鉴了Stl库,其巧妙的在构造V类型模板参数时,对key传了一个const来解决这个问题。
RBTree< K,pair<const K,V>,MapOfVal> _t ;
  1. 对于Set来说,其值也是不可修改的。
    如果允许修改,则就不能满足它是中序有序的了。
    这里通过定义迭代器来实现。
    它直接将const_iterator 定义为 iterator来保证不可修改。
typedef typename RBTree<K, K, SetOfVal>::const_iterator iterator;
typedef typename RBTree<K, K, SetOfVal>::const_iterator const_iterator;

iterator begin()const
{
	return _t.begin();
}

iterator end()const
{
	return _t.end();
}

注意这里我们在写普通迭代器的begin,end时,需要让变量加一个const(如果不加,则它会调用红黑树中普通迭代器的begin和end,从而报错)

困难三、红黑树中迭代器的++和–

首先我们规定begin返回这棵树中 中序遍历的第一个结点。
end返回空指针。
在这里插入图片描述

1.++

对于一个结点来说,++就是要找它在中序遍历中的下一个结点,我们令这个结点为*p。
两种情况

  1. 如果这个结点有右孩子,则p = 右子树中最小的那个结点。即右子树的最左边结点。
  2. 如果这个结点没有右孩子,则令parent = p->parent。
    如果 p为parent的左孩子,表示p这颗树已经遍历完成,则下一个应该为parent。
    如果p为parent的右孩子,则表示parent这颗子树已经全部遍历完成。
    令p = parent, parent = parent->parent ,继续向上遍历。(最后若根节点,则返回nullptr)
Self& operator++()
{
	if (_node->_right != nullptr)
	{
		Node* p = _node->_right;
		while (p->_left != nullptr)
			p = p->_left;
		_node = p;
		return *this;
	}
	else
	{
		Node* parent = _node->_parent;
		while (parent != nullptr)
		{
			if (parent->_left == _node)
			{
				_node = parent;
				return *this;
			}
			else
			{
				_node = parent;
				parent = parent->_parent;
			}
		}
		_node = nullptr;
		return *this;
	}
}

Self operator++(int)
{
	Self temp(_node);
	++*this;
	return temp;
}

2.- -

减减的逻辑和加加刚好是相反的。
去判断有没有左孩子,若有,则为左孩子的最右结点。
若没有,则取判断parent和p的关系。
在此不过多赘述。

Self& operator--()
{
	if (_node->_left != nullptr)
	{
		Node* p = _node->_left;
		while (p->_right != nullptr)
			p = p->_right;
		_node = p;
		return *this;
	}
	else
	{
		Node* parent = _node->_parent;
		while (parent != nullptr)
		{
			if (parent->_right == _node)
			{
				_node = parent;
				return *this;
			}
			else
			{
				_node = parent;
				parent = parent->_parent;
			}
		}

	}
}

Self operator--(int)
{
	Self temp(_node);
	--*this;
	return temp;
}

困难四、map中[ ] 运算符重载的实现

在这里插入图片描述
我们可知如何k已经存在于Map中,则返回它Value的引用。
如果不存在,我们可知需要将其先插入进去,然后返回它Value的引用。
而Insert函数的返回值是一个pair类型,其first为一个指向该元素的迭代器,second是一个bool类型,表示插入成功与否。
所以,我们需要先修改Insert的其返回值。

1.修改红黑树以及Map和Set中insert的返回值

1.修改set
pair<iterator,bool> insert(const K& data)
{
	pair<typename RBTree<K, K, SetOfVal>::iterator, bool> ret = _t.Insert(data);
	return pair<iterator, bool>(ret.first, ret.second);
}

这里需要注意,set中的iterator其实是一个const_iterator。
而红黑树返回的是一个正常的iterator,如果直接这样写会报错。
所以我们需要在迭代器中写一个拷贝构造函数。
其作用是

  1. 在我们需要正常迭代器时进行拷贝构造。
  2. 在我们需要const迭代器时用正常迭代器来初始化const迭代器。
typedef __RBTreeIterator<T, T*, T&> iterator;

__RBTreeIterator(const iterator& it)
	:_node(it._node)
{}

注意这里iterator直接用T,T*,T&构造的,所以它肯定是一个正常迭代器。

2.修改Map
pair<iterator,bool> insert(const pair<const K, V>& kv)
{
	return _t.Insert(kv);
}

直接调用即可

2.[ ]的重载

V& operator[](const  K& data )
{
	pair<iterator, bool> ret = _t.Insert(make_pair(data,V()));
	return ret.first->second;
}

注意map的插入是一个pair类型,所以对于Value我们传了一个默认值进去。

总结

以上就是Map和Set模拟实现中的主要问题所在。
完整版代码存放在Git-ee上:Map,Set模拟实现
本人小白一枚,有问题还望各位大佬指正!!!

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

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

相关文章

Spring Boot入门指南:轻松构建高效Spring应用(四)

Spring Boot入门指南&#xff1a;轻松构建高效Spring应用&#xff08;三&#xff09;-CSDN博客 一.传递参数 7.传递数组 1.在IDEA中写出处理代码 2.打开postman 或者这样:key相同时&#xff0c;也会组成一个数组。 8. 传递集合 如果像数组传参一样传递集合&#xff0c;可能…

Transformer合集

资料 位置编码&#xff1a;https://zhuanlan.zhihu.com/p/454482273 自注意力&#xff1a;https://zhuanlan.zhihu.com/p/455399791 LN&#xff1a;https://zhuanlan.zhihu.com/p/456863215 ResNet&#xff1a;https://zhuanlan.zhihu.com/p/459065530 Subword Tokenizati…

重生之我们在ES顶端相遇第5章-常用字段类型

思维导图 前置 在第4章&#xff0c;我们提到了 keyword&#xff08;一笔带过&#xff09;。在本章&#xff0c;我们将介绍 ES 的字段类型。全面的带大家了解 ES 各个字段类型的使用场景。 字段类型 ES 支持以下字段类型&#xff08;仅介绍开发中常用&#xff0c;更多内容请自…

接口自动化测试框架实战-4-日志方法封装

上一小节我们讲解了文件读写方法的封装方法,本小节我们完成一下框架日志的封装方法。 首先我们讲解一下日志封装和日志记录有哪些用处? 更加方便的设置日志的格式和输出方式全局方法可以各个类/函数中都能统一调用分类记录接口用例执行日志,方便嵌入测试报告错误日志提示,…

乘云数字受邀Zabbix MeetUp济南站,分享《DataBuff在打造可观测性数据底座上的探索》

7月20日&#xff0c;Zabbix主办的MeetUp线下活动在济南圆满举行&#xff0c;众多技术大咖汇集现场&#xff0c;交流技术知识、分享先进的思想。乘云数字受邀参加此次盛宴&#xff0c;创始人兼CEO向成钢在现场发表了关于“DataBuff在打造可观测性数据底座上的探索”的主题演讲。…

【多模态】CLIP-KD: An Empirical Study of CLIP Model Distillation

论文&#xff1a;CLIP-KD: An Empirical Study of CLIP Model Distillation 链接&#xff1a;https://arxiv.org/pdf/2307.12732 CVPR 2024 Introduction Motivation&#xff1a;使用大的Teacher CLIP模型有监督蒸馏小CLIP模型&#xff0c;出发点基于在资源受限的应用中&…

NFTScan 浏览器现已支持 .mint 域名搜索功能!

近日&#xff0c;NFT 数据基础设施 NFTScan 浏览器现已支持用户输入 .mint 域名进行 Mint Blockchain 网络钱包地址的搜索查询&#xff0c; NFTScan 用户能够轻松地使用域名追踪 NFT 交易&#xff0c;为 NFT 钱包地址相关的搜索查询功能增加透明度和便利性。 NFTScan explorer…

C++树形结构(2 树的直径)

目录 1.定义&#xff1a; 2.直径的性质&#xff1a; 3.树的直径求解方法&#xff1a; 4.直径端点求解方法&#xff1a; 朴素方法&#xff1a; 优化方法&#xff1a; 5.例题&#xff1a; 6.直径公共点&#xff1a; 7.例题&#xff1a; 8.去掉再加上&#xff1a; 9.例…

Hi3751V560_SELinux

Hi3751V560_SELinux setenforce Enforcing setenforce Permissive(或“setenforce 0”) getenforce V560:demo本身的: [ 13.765161] type=1400 audit(1628821512.905:4): avc: denied { read } for pid=1926 comm="system_server" name="ifindex" d…

vue3前端开发-小兔鲜项目-图片切换效果和动态class

vue3前端开发-小兔鲜项目-图片切换效果和动态class!这次实现的效果是&#xff0c;图片预览效果&#xff0c;根据小图片&#xff0c;来实时改变大图&#xff08;预览&#xff09;的效果。同时让动态的特征class也跟着显示出来。 <script setup> import {ref} from vue // …

【Vue3】响应式数据

【Vue3】响应式数据 背景简介开发环境开发步骤及源码使用 ref 定义基本类型响应式数据使用 reactive 定义对象类型响应式数据使用 ref 定义对象类型响应式数据 ref 和 reactive 的对比使用原则建议 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈…

【C++初阶】string类

【C初阶】string类 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;C&#x1f96d; &#x1f33c;文章目录&#x1f33c; 1. 为什么学习string类&#xff1f; 1.1 C语言中的字符串 1.2 实际中 2. 标准库中的string类 2.1 string类 2.…

day07:用户下单、订单支付

文章目录 地址薄相关相关代码需求分析和设计代码书写 用户下单需求分析和设计代码开发 订单支付微信支付介绍微信支付准备工作如何保证数据安全&#xff1f;如何调用到商户系统 地址薄相关相关代码 需求分析和设计 产品原型接口设计数据库设计 代码书写 地址薄相关代码都是单…

【unity 新手教程 001/100】安装与窗口布局介绍

欢迎关注 、订阅专栏 【unity 新手教程】谢谢你的支持&#xff01;&#x1f49c;&#x1f49c; Unity下载与安装 &#x1f449;点击跳转详细图文步骤&#xff1a;Unity Hub Unity 编辑器 窗口布局&#xff1a; Hierarchy: 层级窗口 | 默认 Sample Scene (main camera、direc…

三星Unpacked发布会即将举行:有新款折叠屏手机,还有智能戒指

随着7月的脚步渐近&#xff0c;科技界的目光再次聚焦于三星&#xff0c;它即将在法国巴黎举办今年的第二场Unpacked发布会。这不仅是一场新品的展示&#xff0c;更是三星对创新科技的一次深刻诠释。 从Galaxy Z Fold 6的全新设计&#xff0c;到Galaxy Z Flip 6的显著升级&…

MySQL数据库练习(四)

1.建库建表 # 创建数据库 create database mydb15_indexstu;# use mydb15_indexstu;# 学生表student&#xff0c;定义主键&#xff0c;姓名不能重名&#xff0c;性别只能输入男或女&#xff0c;所在系的默认值是“计算机”&#xff0c;结构如下:student(Sno 学号&#xff0c;…

C#中的线性表

什么是线性表 线性表是最简单、最基本、最常用的数据结构。线性表是线性结构的抽象(Abstract),线性结构的特点是结构中的数据元素之间存在一对一的线性关系。这种一对一的关系指的是数据元素之间的位置关系,即:(1)除第一个位置的数据元素外,其它数据元素位置的前面都只有一个数…

基于python的京东VR眼镜口碑情感分析,包括lda和情感分析

第1章 绪论 1.1选题背景 在当今科技发展迅速的时代&#xff0c;虚拟现实&#xff08;VR&#xff09;技术作为一种前沿的数字体验方式受到越来越多人的关注。京东作为中国领先的电商平台&#xff0c;推出的VR眼镜备受消费者关注。通过对京东VR眼镜口碑进行情感分析&#xff0c…

2022 年中高职组“网络安全”赛项-海南省省竞赛任务书-1-B模块-B-4Web渗透测试

前言 本章节我将尝试操作B-4模块的渗透测试&#xff0c;搭建环境很难&#xff0c;还望大家点点赞多多支持&#xff01; 任务概览 最后4、5、6有一定的难度。 环境要求 kali Linux192.168.41.2Web服务器&#xff08;假设为PYsystem 2020 模拟平台&#xff09;192.168.41.7交换…

AGV平面坐标系变换公式及实例

1、AGV坐标系简介 如上图&#xff0c;小车前后对角是有激光雷达的&#xff0c;其坐标系称为激光坐标系&#xff0c;采用极坐标系体现。中间为车体坐标系&#xff0c;激光坐标系相对于车体坐标系关系不变&#xff1b;左下角是地图坐标系&#xff0c;小车扫图后&#xff0c;建立的…