C++ - 使用RBTree来封装Set与Map

news2024/12/23 17:39:00

前面的文章中我们简单的实现了一个红黑树,实现了它的插入的功能,在本文中我们来对其进行修改并构建Set与Map。

下面我们来从源码中截取一点有关Map与Set的代码;

可以看出,在STL30中构建Map与Set使用的是同一个红黑树模板,只是传入了不同的参数。然后我们再来看一下里面红黑树的一些代码,可以看出在set中传入了两个key,在map中传入了一个key一个pair。

set<K> -> rb_tree<K,K>

map<K,V> -> rb_tree<K,pair<const K,V>>

第一个模板参数单独拿到K的类型,因为find与erase这些接口函数参数是K;

第二个模板参数决定了树的节点里面存储了什么类型的数据。

下面我们来进行封装:

仿函数

将结点先存储的数据从pair变为模板类型T;

template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	T _data;
	Colour _col;

	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED) 
	{}
};

然后我们会遇到的问题就是在插入中,之前我们使用的是pair类型的数据,因此插入取的值为kv.first;但是由于Map与Set需要对红黑树的模板进行复用,在红黑树中我们树不清楚使用该模板的到底是Map还是Set,但是在上一层中就可以获取这个消息,所以这里就需要我们引入一个仿函数,通过在Map与Set中仿函数传入红黑树中来获取Set与Map中的K值,这样我们就可以使用K值来进行比较。

struct MapKeyOfT
{
	const K& operator()(const pair<const K, V>& kv)
	{
		return kv.first;
	}
};

struct SetKeyOfT
{
	const K& operator()(const K& key)
	{
		return key;
	}
};

除了上面的,我们还可以编写仿函数来控制Map与Set的数据插入的策略。 

迭代器

下面我们来继续看一下迭代器

迭代器也与我们之前学习的链表的迭代器相类似不过多赘述,下面我们来讲解一下红黑树迭代器特有的部分,迭代器的++与--如何实现:红黑树要走中序,begin()位置的结点就是整棵树最左边的结点返回的地址,end结点在这里我们取的是空节点。

typedef __RBTreeIterator<T, T&, T*> itertaor;
typedef __RBTreeIterator<T, const T&, const T*> const_itertaor;

itertaor begin()
{
	Node* cur = _root;
	while (cur && cur->_left)
	{
		cur = cur->_left;
	}

	return itertaor(cur);
}

itertaor end()
{
	return itertaor(nullptr); // end是最后一个节点的下一个数据
}

在这里编写迭代器的时候还需要注意一点就是:取类模板的内嵌类型需要添加typename,否则编译器不知道这个是类型还是静态变量。

接着我们继续编写++的代码, 以上图为例子,当处于1号节点的时候已经处于最左的结点,下一步就是寻找右子树的最左结点。

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

	_node = subLeft;
}

然后就是当右子树为空时,此时如果该节点是父亲的左节点,那么下一个结点就是父亲节点,如果该节点是父亲的右节点,那么说明父亲这棵子树全部遍历完毕,下一个节点就是祖父节点。

else
{
	// 右子树为空,沿着根的结点,找孩子是父亲左的那个祖先
	Node* parent = _node->_parent;
	while (parent && _node == parent->_right)
	{
		_node = parent;
		parent = parent->_parent;
	}
	
	_node = parent;
}

有++的话--也是同样的方法,通过右子树 根 左子树的顺序进行遍历,就可以得到--的效果。

Map中[]的重载

在Map中还有着[]的方法,使用[]的操作是,如果没有该数据就可以将该数据的key值进行插入,如果有该数据,就可以返回该数据的key值对应的value值。在红黑树中就需要对插入的返回值进行修改变为 pair<iterator, bool> 类型,然后就可以使用Map对[]进行重载。

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

Set中的使用普通迭代器构造const迭代器

在Set中还有这这样的一个问题,当我们使用const迭代器的时候我们是无法对key的值进行修改的,这是我们都知道的,但是当我们使用普通迭代器的时候却发现在这个key值,能够被修改。这种情况是不正确的,因此就可以使用const迭代器来构建普通迭代器,但此时就会有一个问题:

错误 C2440  “return”: 无法从“std::pair<__RBTreeIterator<T,T &,T *>,bool>”转换为“std::pair<__RBTreeIterator<T,const T &,const T *>,bool>”      

这是因为在我们的红黑树代码中只有下面的构造函数,

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

当构建迭代器的begin的时候返回值是一个const迭代器,而return的只是一个普通的迭代器,因此就会发生报错。

const_iterator begin() const 
{
	Node* cur = _root;
	while (cur && cur->_left)
	{
		cur = cur->_left;
	}

	return iterator(cur);
}

所以我们可以在红黑树中这样来编写代码:

typedef __RBTreeIterator<T, Ref, Ptr> Self;	
typedef __RBTreeIterator<T, T&, T*> iterator;
typedef __RBTreeIterator<T, const T&, const T*> const_iterator;

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

分别初始化一个普通迭代器,一个const迭代器和Self,并且编写下面的构造函数,当迭代器是普通迭代器的时候下面的构造函数就是一个拷贝构造函数,当迭代器是const迭代器的时候下面的构造就是一个普通的构造函数,使用迭代器来构造const迭代器。从而可以达到上述的效果。

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

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

相关文章

开箱即用!AI模型库全新发布,一站式在线开发部署!

人工智能学习与实训社区飞桨AI Studio模型库全新升级&#xff01;支持模型创建、上传、托管并一键调用模型线上开发部署&#xff01; 飞桨AI Studio已接入飞桨生态特色模型库&#xff0c;全面覆盖模型领域&#xff0c;包含文心系列大模型、CV、NLP、语音、科学计算、量子计算等…

Linux常用命令——hwclock命令

在线Linux命令查询工具 hwclock 显示与设定硬件时钟 补充说明 hwclock命令是一个硬件时钟访问工具&#xff0c;它可以显示当前时间、设置硬件时钟的时间和设置硬件时钟为系统时间&#xff0c;也可设置系统时间为硬件时钟的时间。 在Linux中有硬件时钟与系统时钟等两种时钟。…

XR文字输入技术

VR/AR/MR&#xff08;统称为XR&#xff09;有望成为个人电脑、手机之后的下一代计算平台&#xff0c;元宇宙的硬件入口。 近年来XR发展迅猛&#xff0c;不仅可以应用于游戏、影视领域&#xff0c;还在军事、医疗、办公、教育等领域有巨大的发展前景。然而相比于XR强大的显示技…

IPWorks IPC .NET 2022.0.85 Crack

IPWorks IPC .NET一个组件库&#xff0c;用于通过命名管道向应用程序添加进程间通信 &#xff08;IPC&#xff09; 功能。包括客户端、服务器和外部进程执行组件&#xff0c;用于进程之间的简单对等通信。 最新的IPWorks IPC现已上市&#xff01;最新版本的 IPWorks IPC 具有现…

我用ChatGPT搞懂GPT技术原理,只问了30个问题,这是极致的学习体验!

自己前段时间写了一篇文章《问了ChatGPT 上百个问题后&#xff0c;我断定ChatGPT可以重塑学习范式&#xff01;》&#xff0c;讲了使用ChatGPT的感受&#xff0c;最近我开始学习GPT的技术原理&#xff0c;原因有三个&#xff1a; 1、工作中有可能要用到GPT&#xff0c;理解GPT的…

智见|中国能建中电工程罗必雄:数能融合为数字中国夯实底座

出品|网易科技《智见访谈》 作者&#xff5c;赵芙瑶 编辑&#xff5c;丁广胜 数字化浪潮的风&#xff0c;吹到了能源结构转型领域。 中国作为全球最大的能源生产国和消费国&#xff0c;正积极推动能源行业的数字化和智能化建设。数字化智能化升级在能源产业中被视为一项重要的战…

Go设计模式--解释器模式

大家好&#xff0c;这里是每周都在陪你一起进步的网管&#xff5e;&#xff01;今天继续学习设计模式—解释器模式 解释器模式是一种行为设计模式&#xff0c;可以用来在程序里创建针对一个特点领域语言的解释器&#xff0c;用于处理解释领域语言中的语句。换句话说&#xff0c…

转转前端周刊第六十五期

转转前端周刊 本刊意在将整理业界精华文章给大家&#xff0c;期望大家一起打开视野 如果你有发现一些精华文章想和更多人分享&#xff0c;可以点击我们的公众号名称&#xff0c;将文章链接和你的解读文案发给我们&#xff01;我们会对内容进行筛选和审核&#xff0c;保留你的推…

基于POM模式应用Playwright进行自动化测试

下方查看历史精选文章 重磅发布 - 自动化框架基础指南pdfv1.1大数据测试过程、策略及挑战 测试框架原理&#xff0c;构建成功的基石 在自动化测试工作之前&#xff0c;你应该知道的10条建议 在自动化测试中&#xff0c;重要的不是工具 前言 在现代软件开发中&#xff0c;自动化…

【Linux】Centos安装Redis数据库并内网穿透远程连接

文章目录 前言1. Linux(centos8)安装redis数据库2. 配置redis数据库3. 内网穿透3.1 安装cpolar内网穿透3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址4.1 保留一个固定tcp地址4.2 配置固定TCP地址4.3 使用固定的tcp地址连接 前言 Redis作为一款高速缓存的key value键值对的…

springboot+jsp家政预约服务管理系统

智慧家政在线预约管理系统为用户提供家政服务预约服务&#xff0c;系统包括前台和后台。 前台用户可以先进行注册&#xff0c;填写个人的基本信息提交到服务器&#xff0c;服务器把数据保存到数据库。管理员对会员的信息进行验证后&#xff0c;会员通过验证后的用户名和密码进行…

vlc搭建rtsp直播Demo ffmpeg + nginx + flv.js实现rtsp网页播放Demo

文章目录 学习链接本地视频文件作为数据源推流步骤拉流步骤 本地摄像头作为数据源拉流步骤 vlc ffmpeg nginx flv.js 实现网页视频直播概括vlc打开摄像头&#xff0c;提供rtsp视频流nginx配置http-flv.conf的nginx配置文件 ffmpeg将rtsp视频流转为rtmp&#xff0c;推送给ngi…

硬文:为什么中本聪不给比特币设计最终性?

* * * 原创&#xff1a;刘教链 * * * 昨日刘教链公众号刊发原创文章《论比特币和以太坊的“宕机”》之后&#xff0c;引发了一些对区块链工作原理感兴趣的朋友的热烈讨论。其中&#xff0c;魔笛手社区群主Claire提了一个好问题&#xff1a;为什么中本聪不给比特币设计最终性&a…

Babylon.js实战WebXR/元宇宙

从 VR 音频实验到街机上的 VR 休闲游戏&#xff0c;再到使用 AR 或 VR 创建新协作方式的更严肃用途&#xff0c;阅读本文后&#xff0c;你应该对今天可以做什么有很好的了解。 事实上&#xff0c;在本文中&#xff0c;我将分享我一直致力于使用 Babylon.js 构建沉浸式或增强现…

Towards Open-Set Object Detection and Discovery(论文翻译)

Towards Open-Set Object Detection and Discovery 摘要 随着人类对知识的不断追求&#xff0c;开集目标检测&#xff08;OSOD&#xff09;被设计用于识别动态世界中的未知目标。然而&#xff0c;当前设置的一个问题是&#xff0c;所有预测的未知对象共享相同的类别为“未知”…

Kali-linux使用Metasploitable操作系统

Metasploitable是一款基于Ubuntu Linux的操作系统。该系统是一个虚拟机文件&#xff0c;从http://sourceforge.net/projects/metasploitable/files/Metasploitable2/网站下载解压之后可以直接使用&#xff0c;无需安装。由于基于Ubuntu&#xff0c;所以Metasploitable使用起来十…

卧龙、凤雏!两源码学得一,代码质量都不会差!

作者&#xff1a;小傅哥 博客&#xff1a;https://bugstack.cn 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 有人问我&#xff0c;编程能力怎么提升&#xff0c;我说学源码学的。他有问我&#xff0c;是不学 Spring 源码比学 MyBatis 更好…

ESP32 event loop(事件循环)(17)

提示&#xff1a;本博客作为学习笔记&#xff0c;有错误的地方希望指正&#xff0c;主要参考乐鑫技术手册说明结合实例代码分析&#xff0c;结合理论知识学习后示例分析以及常见问题说明。 文章目录 一、ESP32 Event Loop 概述2、使用 esp_event APIs3、默认 event loop4、句柄…

Java进阶-面向对象进阶(抽象类接口内部类)

1 抽象类 1.1 概述 1.1.1 抽象类引入 ​ 父类中的方法&#xff0c;被它的子类们重写&#xff0c;子类各自的实现都不尽相同。那么父类的方法声明和方法主体&#xff0c;只有声明还有意义&#xff0c;而方法主体则没有存在的意义了(因为子类对象会调用自己重写的方法)。换句话…

Marvin - LLM驱动的AI函数开发包【Python】

Marvin是一个用于构建 AI 驱动软件的Python库。 Marvin 的工作是将 AI 直接集成到你的代码库中&#xff0c;使其看起来和感觉起来与任何其他功能一样。 Marvin 引入了一个新概念&#xff0c;称为 AI 函数&#xff08;AI functions&#xff09;。 AI函数与传统函数的不同之处在…