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

一、红黑树迭代器的实现

基本的框架和实现链表的迭代器思路是一样的,都是对指针进行封装处理,然后实现一些基本的运算符重载,最重要的是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.mfbz.cn/a/166538.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Day35力扣打卡

打卡记录 相邻字符不同的最长路径&#xff08;树状DP&#xff09; 链接 若节点也存在父节点的情况下&#xff0c;传入父节点参数&#xff0c;若是遍历到父节点&#xff0c;直接循环里 continue。 class Solution:def longestPath(self, parent: List[int], s: str) -> in…

如何看待人工智能行业发展

随着人工智能技术的飞速发展&#xff0c;这个领域的就业前景也日益广阔。人工智能在各行各业都有广泛的应用&#xff0c;包括医疗、金融、制造业、教育等。因此&#xff0c;对于想要追求高薪、高技能职业的人来说&#xff0c;学习人工智能是一个非常有前景的选择。 首先&#x…

【Python进阶】近200页md文档14大体系知识点,第4篇:linux命令和vim使用

本文从14大模块展示了python高级用的应用。分别有Linux命令&#xff0c;多任务编程、网络编程、Http协议和静态Web编程、htmlcss、JavaScript、jQuery、MySql数据库的各种用法、python的闭包和装饰器、mini-web框架、正则表达式等相关文章的详细讲述。 全套Python进阶笔记地址…

阿里云ECS11月销量王 99元/年

这一波好像真没得说&#xff0c;老用户居然都有份&#xff0c;买来练习、测试冒似已经够了&#xff01; 阿里云ECS11月销量王 99元/年 2核2G 3M固定带宽不限流量&#xff0c;新老同享&#xff0c;新购、续费同价&#xff0c;开发必备&#xff01; 活动规则 云服务器ECS 云创季…

读像火箭科学家一样思考笔记03_第一性原理(上)

1. 思维的两种障碍 1.1. 为什么知识会成为一种缺陷而非一种美德 1.1.1. 知识是一种美德 1.1.2. 知识同样的特质也会把它变成一种缺点 1.1.3. 知识确实是个好东西&#xff0c;但知识的作用应该是给人们提供信息&#xff0c;而不是起约束作用 1.1.4. 知识应该启发智慧&#…

程序员告诉你:人工智能是什么?

随着科技的快速发展&#xff0c;人工智能这个词汇已经逐渐融入了我们的日常生活。然而&#xff0c;对于大多数人来说&#xff0c;人工智能仍然是一个相对模糊的概念。 首先&#xff0c;让我们从人工智能的定义开始。人工智能是一种模拟人类智能的技术&#xff0c;它涵盖了多个领…

【Flink】系统架构

DataStream API 将你的应用构建为一个 job graph&#xff0c;并附加到 StreamExecutionEnvironment 。当调用 env.execute() 时此 graph 就被打包并发送到 JobManager 上&#xff0c;后者对作业并行处理并将其子任务分发给 Task Manager 来执行。每个作业的并行子任务将在 task…

微服务调用链路追踪

概述 本文介绍微服务调用链路追踪&#xff0c;涉及技术有&#xff1a;sleuth和zipkin。sleuth负责追踪调用链路数据&#xff0c;zipkin负责调用链路数据可视化展现。 本文的操作是在 服务网关实践 的基础上进行。 环境说明 jdk1.8 maven3.6.3 mysql8 spring cloud2021.0.8 …

Linux socket编程(4):服务端fork之僵尸进程的处理

在上一节利用fork实现服务端与多个客户端建立连接中&#xff0c;我们使用fork函数来实现服务端既可以accept新的客户端连接请求&#xff0c;又可以接收已连接上的客户端发来的消息。但在Linux中&#xff0c;在子进程终止后&#xff0c;父进程需要处理该子进程的终止状&#xff…

人力资源小程序

人力资源管理对于企业的运营至关重要&#xff0c;而如今随着科技的发展&#xff0c;制作一个人力资源小程序已经变得非常简单和便捷。在本文中&#xff0c;我们将为您介绍如何通过乔拓云网制作一个人力资源小程序&#xff0c;只需五个简单的步骤。 第一步&#xff1a;注册登录乔…

练习题——【学习补档】库函数的模拟实现

各种库函数的模拟实现 一、模拟实现strlen1.地址-地址型2.递归型3.计数器型 二、模拟实现strcpy三、模拟实现strcmp四、模拟实现strcat五、模拟实现strstr 一、模拟实现strlen 模拟实现strlen有三种方法 1.地址-地址型 2.递归型 3.计数器型1.地址-地址型 // //1.地址-地址型 …

学霸教你自学人工智能

在这个信息爆炸的时代&#xff0c;人工智能已经渗透到我们生活的方方面面。无论是语音助手、自动驾驶汽车&#xff0c;还是医疗诊断&#xff0c;人工智能都在发挥着越来越重要的作用。如果你对人工智能充满热情&#xff0c;希望在这个领域有所建树&#xff0c;那么&#xff0c;…

STM32CubeMX学习笔记-CAN接口使用

STM32CubeMX学习笔记-CAN接口使用 CAN总线传输协议1.CAN 总线传输特点2.位时序和波特率3.帧的种类4.标准格式数据帧和遥控帧从STM32F407参考手册中可以看出主要特性如下CAN模块基本控制函数CAN模块消息发送CAN模块消息接收标识符筛选发送中断的事件源和回调函数 CubeMX项目设置…

庖丁解牛:NIO核心概念与机制详解 04 _ 分散和聚集

文章目录 Pre概述分散/聚集 I/O分散/聚集的应用聚集写入Code Pre 庖丁解牛&#xff1a;NIO核心概念与机制详解 01 庖丁解牛&#xff1a;NIO核心概念与机制详解 02 _ 缓冲区的细节实现 庖丁解牛&#xff1a;NIO核心概念与机制详解 03 _ 缓冲区分配、包装和分片 概述 分散/聚…

C++二分查找算法:有序矩阵中的第 k 个最小数组和

本文涉及的基础知识点 二分查找算法合集 本题的简化 C二分查找算法&#xff1a;查找和最小的 K 对数字 十分接近m恒等于2 题目 给你一个 m * n 的矩阵 mat&#xff0c;以及一个整数 k &#xff0c;矩阵中的每一行都以非递减的顺序排列。 你可以从每一行中选出 1 个元素形成…

FPGA_IIC代码-正点原子 野火 小梅哥 特权同学对比写法(3)

FPGA_IIC代码-正点原子 野火 小梅哥 特权同学对比写法&#xff08;3&#xff09; 工程目的IIC时序图IIC 读写操作方法汇总正点原子IIC实验工程整体框图和模块功能简介&#xff0c;如表下图所示&#xff1a; IIC 驱动模块设计时钟规划状态跳转流程单次写操作的波形图如下图所示&…

程序员带你入门人工智能

随着人工智能技术的飞速发展&#xff0c;越来越多的程序员开始关注并学习人工智能。作为程序员&#xff0c;我们可能会对如何开始了解人工智能感到困惑。今天&#xff0c;我将向大家介绍一些如何通过自学了解人工智能的经验和方法&#xff0c;帮助大家更好地入门这个充满挑战和…

【Java集合】聊聊Hashmap的哈希函数、扩容、树化

哈希函数 hashmap是开发中常用的一个集合&#xff0c;除了一些基本的属性、put、get等流程&#xff0c;本篇文章主要介绍下哈希函数、扩容、树化的一些细节。 而hash函数就是hashmap的重中之重。 static final int hash(Object key) {int h;return (key null) ? 0 : (h key…

YOLOv8改进 | EIoU、SIoU、WIoU、DIoU、FoucsIOU等二十余种损失函数

一、本文介绍 这篇文章介绍了YOLOv8的重大改进&#xff0c;特别是在损失函数方面的创新。它不仅包括了多种IoU损失函数的改进和变体&#xff0c;如SIoU、WIoU、GIoU、DIoU、EIOU、CIoU&#xff0c;还融合了“Focus”思想&#xff0c;创造了一系列新的损失函数。这些组合形式的…

Halcon Solution Guide I basics(1): Guide to Halcon Methods(Halcon解决方案)

文章目录 文章专栏前言文章解读基础解决方案字符串格式化 文章专栏 Halcon开发 前言 今天来看Halcon的第一章内容&#xff0c;Halcon解决方案 文章解读 基础解决方案 Halcon大部分的应用都使用了三种常用的算子应用用于图像的预处理。 Image Acquisition&#xff1a;图像加…
最新文章