C++模拟实现——红黑树封装set和map

news2024/9/23 19:24:55

一、红黑树迭代器的实现

基本的框架和实现链表的迭代器思路是一样的,都是对指针进行封装处理,然后实现一些基本的运算符重载,最重要的是operator++,需要不递归的实现走中序的规则,这里只实现那最核心的几个基本功能,用遍历和插入值去测试,其余的一些零零散散的功能就不进行实现了

基本框架

operator++的实现

按照中序遍历的规则,首先是走左子树,然后是根,然后是右子树,从begin位置开始,可以认为此时是最左边的那一个,此时的++,是要往该节点的右边去遍历,而且是右边的最左边,因此要先确定,此时是否有右边,然后走到右边的最左边,就是下一个要遍历的节点,如果右边为空,则我们说明该节点已经遍历结束了,此时往上走找到parent,需要判断parent是否已经遍历过,则需要再判断parent是否是其上一个节点的右边,如果是右边,则说明此时parent位置已经被遍历过且右子树遍历结束,需要继续向上走

ps:operator--的思路和++是一样的,不过啥反过来走,右子树 根 左子树,这里不过多分析

参考代码

template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef __RBTreeIterator<T, Ref, Ptr> Self;
	Node* _node;

	__RBTreeIterator(Node* node)
		:_node(node)
	{}

	// 1、typedef __RBTreeIterator<T, T&, T*> itertaor;  拷贝构造
	// 2、 typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;
	//  支持普通迭代器构造const迭代器的构造函数

	__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it)
		:_node(it._node)
	{}

	Ref operator*()
	{
		return _node->_data;
	}

	Ptr operator->()
	{
		return &_node->_data;
	}

	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}

	Self& operator++()
	{
		if (_node->_right)
		{
			// 1、右不为空,下一个就是右子树的最左节点
			Node* subLeft = _node->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}

			_node = subLeft;
		}
		else
		{
			// 2、右为空,沿着到根的路径,找孩子是父亲左的那个祖先
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	Self& operator--()
	{
		if (_node->_left)
		{
			// 1、左不为空,找左子树最右节点
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}

			_node = subRight;
		}
		else
		{
			// 2、左为空,孩子是父亲的右的那个祖先
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}
};

二、封装set和map

由于之前模拟实现的红黑树,是为了模拟实现和学习其中的核心功能,也就是如何完成插入,以及明白其算法原理,所以在其他的细节上,与库里的对比,做了很多的省略,本次将用自己实现的红黑树,通过封装红黑树,模拟实现出set和map,加深对set和map的理解和底层实现

我们对红黑树的改造,目的是为了兼容set和map的复用,因此,得先了解,set和map具体的使用区别

1.对红黑树的基本改造

虽然底层都是搜索二叉树,但是节点内存的值类型是不同的,从使用的角度来看,可以认为k模型每个节点存的就是一个key,搜索树的顺序和规则也是根据key的大小比较规则去执行的,而kv模型则是每个节点内存着key和value,搜索树以key的大小比较规则其执行,而每个key都有一个关联性很强的value,所以,在节点的数据类型上看,kv模型的节点数据类型pair类型的

此时,红黑树需要兼容任意类型的模板参数都能实现相同的比较规则,都是找到key去比较,则需要像以下类模板定义:

template<class K,class T,class KeyOfT>

接下来就是,将所有关于比较的部分,需要换上仿函数,通过仿函数去取得key,第一个参数K代表key,当一些需要使用到key类型的地方(返回参数等等)时使用,修改过后,对set和map的基本封装就没有问题了,至少可以实现插入功能了,各自将框架搭起来,然后复用Insert进行测试

2.对迭代器的封装

(1)set迭代器的封装

set的迭代器要求,无论是iterator还是const_iterator,都不能对值进行修改,因为在set里面存着的值就是key,key不允许被修改,所以我们封装时,直接将两种迭代器都用红黑树的const_iterator去复用,注意:typedef一个类型名的时候,需要在前面加上typename

但是,如果红黑树的迭代器实现部分,没有将普通迭代器转换成常量迭代器的函数,则直接复用会报错

说的是在使用迭代器的时候,返回的迭代器类型是普通迭代器类型,但是返回参数类型的声明却是常量迭代器类型,无法转换,这是由于我们使用的是非const对象调用红黑树的迭代器,则红黑树会则会穿一个普通迭代器,但是我们iterator的类型实际是const_iterator,因此无法转化报错,解决这个问题的办法,是在红黑树的迭代器实现部分,提供一个能够将普通迭代器转化成const迭代器的函数

当传参为const类型的迭代器时,该函数为拷贝构造,当参数是普通迭代器时,则那够构造一个const类型的迭代器返回

		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}

        

(2)map的迭代器封装

map的值是pair类型,first是key,不允许修改,second是value,允许修改,所以map的普通迭代器是允许修改值的,但要保证key不能被修改,在传参时传pair<const K,V>

3.map的operator[ ]的实现

实现[ ]的重载,需要先对红黑树中插入函数的返回值进行改造,之前为了简化,因此用bool值作为返回值,现在需要完整的实现它,返回值为pair类型,first为插入成功位置的迭代器,若是插入失败,则说明书中已经有了一个值,此时first返回已有的值的迭代器,second则是bool值,表示返回插入是否成功

改造结束后,根据[ ]功能实现,这个部分在之前有做过分析,不详细分析,提供参考代码

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

三、测试

以上就是模拟封装set和map时,需要注意的部分,接下来就是通过一些最基本的测试去测试set和map是否能实现一些基本用法,下面提供几个用于测试的代码

set测试迭代器和插入

	void test_set1()//测试迭代器和插入
	{
		srand(time(0));
		const size_t N = 1000;
		set<int> s;
		for (int i = 0; i < N; i++)
		{
			size_t x = rand() % 100;
			s.Insert(x);
		}
		for (auto e : s)
		{
			cout << e << " ";
		}
		cout << endl;
	}

map测试迭代器和插入

	void test_map1()//测试插入和迭代器
	{
		srand(time(0));
		const size_t N = 1000;
		map<int, int> m;
		for (int i = 0; i < N; i++)
		{
			size_t x = rand() % 100;
			m.Insert(make_pair(x, x));
		}
		for (auto e : m)
		{
			cout << e.first << ":" << e.second << endl;
		}
		cout << endl;
	}

map测试[ ]的重载

	void test_map2()//测试[]的重载
	{
		map<string, string> m;
		m["字符串"] = "string";
		m["清理"] = "clear";
		m["想念"] = "miss";
		m["错过"] = "miss";
		m["错过"] = "miss";

		for (auto e : m)
		{
			cout << e.first << ":" << e.second << endl;
		}
		cout << endl;
	}

总结

本篇模式实现了用红黑树对set和map的封装,以及部分需要注意的难点,还有对红黑树迭代器的实现进行了补充,结合着对set和map迭代器的封装复用去一起整理的思路

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

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

相关文章

Typecho框架漏洞

这里说的框架漏洞只适用于1.2.0版本及以下的版本 这里说的漏洞是xss漏洞&#xff0c;学过渗透的应该都学过&#xff0c;我在这里就不过多阐述了&#xff0c;下面我们直接进入正题 直接在这个地方插入网址&#xff0c;后面再接上html代码即可&#xff0c;代码如下&#xff1a; …

软件测试: 测试用例

一. 软件测试四要素 测试环境,操作步骤,测试数据,预期结果 二. 基于需求进行测试用例的设计 基于需求设计测试用例是测试设计和开发测试用例的基础,第一步就要分析测试需求,验证需求是否正确,完整,无二义性,并且逻辑自洽.在需求正确的基础上细化测试需求,从测试需求提炼出一…

高防CDN为什么可以防DDOS攻击

CDN的全称是ContentDeliveryNetwork&#xff0c;即内容分发网络&#xff0c;顾名思义&#xff0c;它是一个分布式节点网络(也称为边缘服务器)&#xff0c;CDN节点具有缓存内容的功能&#xff0c;使用户可以在不获取源服务器数据的情况下就近获取所需内容&#xff0c;提高客户访…

你知道什么是SaaS吗?

你知道什么是SaaS吗&#xff1f; 云服务架构的三个概念 PaaS 英文就是 Platform-as-a-Service&#xff08;平台即服务&#xff09;PaaS&#xff0c;某些时候也叫做中间件。就是把客户采用提供的开发语言和工具&#xff08;例如Java&#xff0c;python, .Net等&#xff09;开…

【Django使用】django经验md文档10大模块。第4期:Django数据库增删改查

Django的主要目的是简便、快速的开发数据库驱动的网站。它强调代码复用&#xff0c;多个组件可以很方便的以"插件"形式服务于整个框架&#xff0c;Django有许多功能强大的第三方插件&#xff0c;你甚至可以很方便的开发出自己的工具包。这使得Django具有很强的可扩展…

【Highway-env】IntersectionEnv代码阅读

文章目录 主要完成任务代码结构1.action space2.default_config3.reward_agent_rewards_agent_reward_reward_rewards小结 4.terminated & truncated5.reset_make_road_make_vehicles_spawn_vehicle 6.step 主要完成任务 IntersectionEnv继承自AbstractEnv,主要完成以下4个…

基于单片机16路抢答器仿真系统

**单片机设计介绍&#xff0c; 基于单片机16路抢答器仿真系统 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的16路抢答器仿真系统是一种用于模拟和实现抢答竞赛的系统。该系统由硬件和软件两部分组成。 硬件方面&am…

单张图像3D重建:原理与PyTorch实现

近年来&#xff0c;深度学习&#xff08;DL&#xff09;在解决图像分类、目标检测、语义分割等 2D 图像任务方面表现出了出色的能力。DL 也不例外&#xff0c;在将其应用于 3D 图形问题方面也取得了巨大进展。 在这篇文章中&#xff0c;我们将探讨最近将深度学习扩展到单图像 3…

CICD 持续集成与持续交付——git

git使用 [rootcicd1 ~]# yum install -y git[rootcicd1 ~]# mkdir demo[rootcicd1 ~]# cd demo/ 初始化版本库 [rootcicd1 demo]# git init 查看状态 [rootcicd1 demo]# git status[rootcicd1 demo]# git status -s #简化输出 [rootcicd1 demo]# echo test > README.md[roo…

计算机毕业设计选题推荐-内蒙古旅游微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

控制您的音乐、视频等媒体内容

跨多个 Chrome 标签页播放音乐或声音 在计算机上打开 Chrome 。在标签页中播放音乐、视频或其他任何有声内容。您可以停留在该标签页上&#xff0c;也可以转到别处。要控制声音&#xff0c;请在右上角点击“媒体控件”图标 。您可暂停播放、转到下一首歌曲/下一个视频&#xf…

六大排序(插入排序、希尔排序、冒泡排序、选择排序、堆排序、快速排序)未完

文章目录 排序一、 排序的概念1.排序&#xff1a;2.稳定性&#xff1a;3.内部排序&#xff1a;4.外部排序&#xff1a; 二、插入排序1.直接插入排序2.希尔排序 三、选择排序1.直接选择排序方法一方法二直接插入排序和直接排序的区别 2.堆排序 四、交换排序1.冒泡排序2.快速排序…

机器视觉系统中的工业镜头的参数

光学倍率 β 焦距 f F值&#xff08;光圈&#xff09;Fno. 数值孔径 NA 工作距离 WD 视场&#xff08;视场角&#xff0c;视野&#xff09; 景深DOF 分辨率、分辨力 MTF 畸变

深入理解注意力机制(上)-起源

一、介绍 近几年自然语言处理有很大的进展&#xff0c;从 2018 年 Google 推出的 BERT&#xff0c;到后来的 GPT、ChatGPT 等&#xff0c;这些模型当时能取得这样的成果&#xff0c;除了庞大的数据量及损害资源外&#xff0c;最重要的是的就是背后的Transformer模型&#xff0c…

电子学会C/C++编程等级考试2022年03月(一级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:双精度浮点数的输入输出 输入一个双精度浮点数,保留8位小数,输出这个浮点数。 时间限制:1000 内存限制:65536输入 只有一行,一个双精度浮点数。输出 一行,保留8位小数的浮点数。样例输入 3.1415926535798932样例输出 3.1…

莹莹API管理系统源码附带两套模板

这是一个API后台管理系统的源码&#xff0c;可以自定义添加接口&#xff0c;并自带两个模板。 环境要求 PHP版本要求高于5.6且低于8.0&#xff0c;已测试通过的版本为7.4。 需要安装PHPSG11加密扩展。 已测试&#xff1a;宝塔/主机亲测成功搭建&#xff01; 安装说明 &am…

算法——动态规划(新)

什么是动态规划&#xff1f; 动态规划算法的基本思想-求解步骤-基本要素和一些经典的动态规划问题【干货】-CSDN博客 一、三步问题 面试题 08.01. 三步问题 - 力扣&#xff08;LeetCode&#xff09; 思路 我们要知道&#xff0c;走楼梯&#xff0c;前三个阶梯步数已经知道&…

基于深度学习的恶意软件检测

恶意软件是指恶意软件犯罪者用来感染个人计算机或整个组织的网络的软件。 它利用目标系统漏洞&#xff0c;例如可以被劫持的合法软件&#xff08;例如浏览器或 Web 应用程序插件&#xff09;中的错误。 恶意软件渗透可能会造成灾难性的后果&#xff0c;包括数据被盗、勒索或网…

原理Redis-动态字符串SDS

动态字符串SDS Redis中保存的Key是字符串&#xff0c;value往往是字符串或者字符串的集合。可见字符串是Redis中最常用的一种数据结构。 不过Redis没有直接使用C语言中的字符串&#xff0c;因为C语言字符串存在很多问题&#xff1a; 获取字符串长度的需要通过运算非二进制安全…

【计算思维】蓝桥杯STEMA 科技素养考试真题及解析 4

1、下列哪个选项填到填到下图空缺处最合适 A、 B、 C、 D、 答案&#xff1a;D 2、按照如下图的规律摆放正方形&#xff0c;第 5 堆正方形的个数是 A、13 B、14 C、15 D、16 答案&#xff1a;D 3、从右面观察下面的立体图形&#xff0c;看到的是 A、 B、 C、 D、 答…