C++ RBTree封装mapset

目录

RBTreeNode的声明

 RBTree结构

 map结构

 set结构

改造红黑树

迭代器类

迭代器成员函数

默认成员函数 

Insert

set

map


RBTreeNode的声明

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){}
};

 RBTree结构

template<class K, class T, class KeyOfType>
class RBTree
{typedef RBTreeNode<T> node;
public:typedef  __RBTreeIterator<T, T&, T*> Iterator;typedef  __RBTreeIterator<T, const T&, const T*> ConstIterator;
private:node* _root = nullptr;
};

 map结构

namespace nineone
{template<class K, class V>class map{struct keyofmap{const K& operator()(const pair<K, V>& kv) const{return kv.first;}};public:private:RBTree<K, pair<const K, V>, keyofmap> rbt;};
}

 set结构

namespace nineone
{template<class K>class set{struct keyofset{const K& operator()(const K& lhs)  const{return lhs;}};public:private:RBTree<K, const K, keyofset> rbt;//这里没有const find说类型不对};
}
  • mapset通过组合的方式复用RBTree
  • 第二个模板参数是用来决定RBTree数的类型
  • 第三个模板参数是为了拿出T里的键,是去实例化的对象的变量里,就是node里的_data
  • 那不是有T和KeyOfType,为什么还需要第一个模板参数呢?因为作为成员函数形参的时候没法从T里拿出K,比如Find就要传进K进去

解释const

  • keyofmap是一个仿函数,用对象去调用,核心部分是operator()操作符的重载
  • 第一个const,为了防止返回的值被改变,从而对一个调用这个仿函数的函数,出现不被预期的结果
  • 第二个const,为了可以使const对象也可以调用
  • 第三个const,是为了防止 keyofmap 类成员函数的改变,那这类现在不是没有吗?这个仿函数的目的就是为了取出对应的K,建议看effectivC++

改造红黑树

迭代器类

template<class T, class Ref, class Ptr>
struct __RBTreeIterator
{typedef RBTreeNode<T> node;typedef  __RBTreeIterator<T, Ref, Ptr> Self;node* _node;__RBTreeIterator( node *const n) :_node(n){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}bool operator!=(const Self& self){return _node != self._node;}Self operator++(int){Self ret = *this;++*this;return ret;}Self operator++(){if (_node && _node->_right != nullptr){node* minright = _node->_right;while (minright->_left){minright = minright->_left; //果然,这里写错成_right了}_node = minright;}else{node* cur = _node;node* parent = cur->_parent;while (parent && parent->_right == cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}
};
  • Ref对应的是T& 或者const T&;Ptr对应的是T* 或者const T*
  • 这是用一个类来封装node*指针,用类对象来使用迭代器
  • 构造里的const;指针是有两部分,第一是指针自身,第二是自己所指向的内容,★也就是说读写的有两部★;一个总结,const往左边找,左边没有往右边找,靠近哪个,哪个就是常量;node* const n这样写是为了防止传进来的指针被改变;如果是const node* n或node const * n那么是指针所指向的内容不能被改变,关键点是:这个指针所指向的内容是常量;而_node是一个读写指针指针,_node所指的内容和指针自身是可以改变的

operator++

  • 走到中序的下一个位置
  • 中序是左子树 根 右子树 ;过当前节点右不为空,就要去找右树的最左节点;如果为空,就说明这个节点走完了,回去看父亲,如果父亲的左指向他,那么父亲这个节点还没走,如果父亲的右指向他,说明父亲节点走完,要向上找,知道找到一个节点,使得parent的left等于cur
  • 注意 对指针的解引用之前,最好提前判空

迭代器成员函数

public:Iterator Begin(){node* minleft = _root;while (minleft && minleft->_left){minleft = minleft->_left;}return Iterator(minleft);}Iterator End(){return Iterator(nullptr); //要有构造}ConstIterator Begin() const{node* minleft = _root;while (minleft && minleft->_left){minleft = minleft->_left;}return ConstIterator(minleft);}ConstIterator End() const{return ConstIterator(nullptr); //要有构造}

默认成员函数 

public:RBTree() = default;RBTree(const RBTree<K, T, KeyOfType>& copy){_root = Copy(copy._root);}RBTree& operator=(RBTree<K, T, KeyOfType> rbt){swap(_root, rbt._root);return *this;}~RBTree(){Destory(_root);_root = nullptr;}
private:void Destory(node* root){if (root == nullptr)return;Destory(root->_left);Destory(root->_right);delete root;}	node* Copy(node* root){if (root == nullptr){return nullptr;}node* newnode = new node(root->_data);newnode->_col = root->_col;newnode->_left = Copy(root->_left);if (newnode->_left)newnode->_left->_parent = newnode;newnode->_right = Copy(root->_right);if (newnode->_right)newnode->_right->_parent = newnode;return newnode;}

类和对象 

  • 这里必须要提供默认构造,因为成员变量声明的时候不能使用()来初始化;为了提供更清晰的内存管理和初始化顺序,成员变量的初始化必须在初始化列表或者构造函数体内
  • 构造函数一定不能是const函数,因为他就是为了初始化用的
  • 赋值重载 这里一定要是swap(_root, rbt._root);不能是 _root = rbt._root;因为这个rbt是一个传值传参,出了这个函数就会被调析构;一定要传引用返回,不然又会调拷贝构造;一定要有返回值,目的是为了可以连续赋值

代码

  • 析构的时候用后序析构
  • 拷贝的时候,不要忘记子连接回父

Insert

pair<Iterator, bool> Insert(const T& data){if (_root == nullptr){_root = new node(data);_root->_col = BLACK;return make_pair(Iterator(_root), true);}KeyOfType keyoftype;node* cur = _root;node* parent = cur;while (cur){if (keyoftype(cur->_data) < keyoftype(data)){parent = cur;cur = cur->_right;}else if (keyoftype(cur->_data) > keyoftype(data)){parent = cur;cur = cur->_left;}else{return make_pair(Iterator(cur), false);}}cur = new node(data);node* newnode = cur;if (keyoftype(parent->_data) < keyoftype(data)){parent->_right = cur;}else{parent->_left = cur;}cur->_parent = parent;while (parent && parent->_col == RED){node* pparent = parent->_parent;if (pparent->_left == parent){node* uncle = pparent->_right;if (uncle && uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;pparent->_col = RED;cur = pparent;parent = pparent->_parent;}else{if (parent->_left == cur){rotateR(pparent);//cur->_col = RED;parent->_col = BLACK;pparent->_col = RED; //这个之前一定是黑}else{rotateL(parent);rotateR(pparent);cur->_col = BLACK;//这竟然写错了pparent->_col = RED;}break;}}else{node* uncle = pparent->_left;if (uncle && uncle->_col == RED){uncle->_col = BLACK;parent->_col = BLACK;pparent->_col = RED;}else{if (parent->_right == cur){rotateL(pparent);parent->_col = BLACK;pparent->_col = RED;}else{rotateR(parent);rotateL(pparent);cur->_col = BLACK;pparent->_col = RED;}break;}}_root->_col = BLACK;}return { Iterator(newnode), true };}
  • 返回值的变化,和取值的变化
  • 取值可以用  KeyOfType  的匿名对象来取

代码

  • return 也可以:pair<Iterator, bool>(newnode, true);  or  make_pair(Iterator(newnode), true)  or make_pair(newnode, true),不建议最后一种写法;effectiv C++建议:应该尽量避免隐式类型转换,以防止潜在的错误和难以维护的代码;回过来看,第三种因为 iterator是一个node*的封装,而不是node* 
  • 列表初始化:一个重要特性是防止窄化转换,比如 防止double 变成 int ;防止隐式类型转换还是要使用explicit
  • explicit只能用于修饰构造函数转换运算符;explicit operator int() const{}这是转换运算符(隐式转换运算符)
  • 不能传引用返回,因为return的是一个临时对象,出作用域会被销毁,导致悬空引用;make_pair也是传值返回,因为他是返回的是pair的匿名对象

set

namespace nineone
{template<class K>class set{struct keyofset{const K& operator()(const K& lhs)  const{return lhs;}};public:typedef typename RBTree<K, const K, keyofset>::Iterator iterator;typedef typename RBTree<K, const K, keyofset>::ConstIterator const_iterator;iterator begin(){return rbt.Begin();}iterator end(){return rbt.End();}const_iterator begin() const{return rbt.Begin();}const_iterator end() const{return rbt.End();}pair<iterator, bool> insert(const K& k){return rbt.Insert(k);}iterator find(const K& key){return rbt.Find(key);}private:RBTree<K, const K, keyofset> rbt;};
}

 解释

  • 通过组合的方式,用RBTree来封装set,隐藏RBTree类实现细节,提供用户简洁的接口
  • typedef前面一定要加 typename;没有实例化的类模板去取内嵌类型,是有异议的,编译器不知道这是成员函数名还是成员变量;iterator一定要是public,因为要在类外使用,对于RBTree里的别名同理

解释:两个别名和成员变量的第二个模板参数必须一样

  • 前提要点,不同类型参数实例化的类模板被视为不同类型,所以不能直接转换(编译器不提供隐式类型转换);
  • 这三个只要有一个不一样就会报类型无法转换的错误;报错的地方在insert或者find return的地方
  • 我写文章的时候再次调试,报错的地方是对的,在const_iterator end() const这里报错,就是这里,返回语句的地方;因为这个rbt对象构建的树节点的K,是const K类型;但是const_iterator的树节点类型是K,导致返回类型与返回语句的类型不一样,且类型不兼容★
  • 如果函数的返回类型与声明里的返回类型无法隐式类型转换类型不兼容),编译器就会报错

解释:第二个模板参数

  • 这个模板参数作用到的是节点和迭代器
  • 但是在ConstIterator里后面两个模板参数不是有const了;编译器会自动去掉多余的const,但是你自己多加会编译报错
  • 第二个参数是const,那么迭代器里,_node的类型是RBTreeNode<const T>*,这是一个普通指针;于指针而言,可以指向不同的RBTreeNode<const T>对象;与对象而言,对象里有部分是可以改变的,只有_data是const;const RBTreeNode<const T>*这样才是一个常量指针
  • 所以_node不会因为T为const导致++不能使用

map

namespace nineone
{template<class K, class V>class map{struct keyofmap{const K& operator()(const pair<K, V>& kv) const{return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, V>::Iterator iterator;typedef typename RBTree<K, pair<const K, V>, V>::ConstIterator const_iterator;pair<iterator, bool> insert(const pair<K, V>& kv){return rbt.Insert(kv);}iterator find(const K& key){return rbt.Find(key);}V& operator[](const K& key){pair<iterator, bool> ret = rbt.Insert(make_pair(key, V()));return ret.first->second;}iterator begin(){return rbt.Begin();}iterator end(){return rbt.End();}const_iterator begin() const{return rbt.Begin();}const_iterator end() const{return rbt.End();}private:RBTree<K, pair<const K, V>, keyofmap> rbt;};
}

 解释insert

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

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

相关文章

非等值连接、等值连接、自然连接

目录 一、笛卡尔积 二、三种连接的关系 三、非等值连接 四、等值连接 五、自然连接 一、笛卡尔积 要理解非等值连接、等值连接、自然连接首先要理解笛卡尔积。 学过《离散数学》的应该很熟悉笛卡尔积。 简单来说&#xff0c;就是有两个集合&#xff0c;其中一个集合中的元…

第五十四周:文献阅读

目录 摘要 Abstract 文献阅读&#xff1a;基于经验模态分解的混合空气质量预测模型 现有问题 提出方法 方法论 1、扩展ARIMA模型 2、经验模态分解&#xff08;EMD&#xff09; 3、截断奇异值分解&#xff08;SVD&#xff09; SE-ARIMA模型 研究实验 1、数据集 2、评…

如何从U盘恢复误删除的文件

在许多情况下&#xff0c;用户可能会发现其U盘上的数据误删&#xff0c;并且无法访问或恢复它。在这篇文章中&#xff0c;我们将看到如何使用命令提示符尝试从U盘恢复损坏的文件和数据。我们还将列出一些免费的U盘恢复软件及其独特的功能&#xff0c;以便在前一种方法无法产生所…

北邮22级信通院DSP:用C++程序实现给定参数下四种滤波器的Butterworth模拟滤波器设计:给定上下截频和衰减系数求H(p)和H(s)

北邮22信通一枚~ 跟随课程进度更新北邮信通院DSP的笔记、代码和文章&#xff0c;欢迎关注~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院DSP_青山入墨雨如画的博客-CSDN博客 目录 一、 核心算法 1.1判断滤波器类型 1.2 带通滤波器BP 1.3带阻滤波器B…

分享:大数据风险检测报告,哪里查询比较好?

随着大数据技术的发展&#xff0c;逐渐被运用到各个领域&#xff0c;基于大数据技术的个人风险检测也就是我们常说的大数据报告在金融环境中运用的十分普遍&#xff0c;那大数据风险检测报告哪里查询比较好呢?本文就为大家简单介绍一下。 大数据风险检测报告查询能查到什么? …

Ubuntu coredump文件的生成并利用gdb查看报错位置

1.开启core文件生成 相关链接&#xff1a;gdb调试core dump使用 - 知乎 (zhihu.com) 相关链接&#xff1a;ubuntu | linux下程序崩溃生成coredump的方法_linux崩溃dump-CSDN博客 &#xff08;1&#xff09;ulimit -c unlimited &#xff08;2&#xff09;echo "/tmp/cor…

在ubuntu中关于驱动得问题:如何将nouveau驱动程序加入黑名单和安装NVIDIA显卡驱动

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、nouveau驱动程序加入黑名单二、安装NVIDIA显卡驱动 一、nouveau驱动程序加入黑名单 (1) 打开黑名单列表文件 终端输入&#xff1a; sudo gedit /etc/modprobe…

HarmonyOS开发之DevEco Studio安装

HUAWEI DevEco Studio是基于IntelliJ IDEA Community开源版本打造&#xff0c;为运行在HarmonyOS和OpenHarmony系统上的应用和服务&#xff08;以下简称应用/服务&#xff09;提供一站式的开发平台。 作为一款开发工具&#xff0c;除了具有基本的代码开发、编译构建及调测等功能…

Linux程序开发(十二):线程与多线程同步互斥实现抢票系统

Tips&#xff1a;"分享是快乐的源泉&#x1f4a7;&#xff0c;在我的博客里&#xff0c;不仅有知识的海洋&#x1f30a;&#xff0c;还有满满的正能量加持&#x1f4aa;&#xff0c;快来和我一起分享这份快乐吧&#x1f60a;&#xff01; 喜欢我的博客的话&#xff0c;记得…

自己手写一个栈【C风格】

#include <iostream> //栈 #define MAX_SIZE 20 #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0typedef int Status;//状态类型 typedef int ElemType;//元素类型typedef struct SqStack {ElemType data[MAX_SIZE];int top; };//初始化&#xff0c;方法1 …

CSAPP(datalab)解析

howManyBits /* howManyBits - 返回用二进制补码表示x所需的最小位数* 示例: howManyBits(12) 5* howManyBits(298) 10* howManyBits(-5) 4* howManyBits(0) 1* howManyBits(-1) 1* howManyBits(0x80000000) …

计算机毕业设计 | SpringBoot社区物业管理系统 小区管理(附源码)

1&#xff0c; 概述 1.1 课题背景 近几年来&#xff0c;随着物业相关的各种信息越来越多&#xff0c;比如报修维修、缴费、车位、访客等信息&#xff0c;对物业管理方面的需求越来越高&#xff0c;我们在工作中越来越多方面需要利用网页端管理系统来进行管理&#xff0c;我们…

WordPress安装插件失败No working transports found

1. 背景&#xff08;Situation&#xff09; WordPress 社区有非常多的主题和插件&#xff0c;大部分人用 WordPress 都是为了这些免费好用的主题和插件。但是今天安装完 WordPress 后安装插件时出现了错误提示&#xff1a;“ 安装失败&#xff1a;下载失败。 No working trans…

Vulhub——CAS 4.1、AppWeb、apisix

文章目录 一、Apereo CAS 4.1&#xff08;反序列化命令执行漏洞&#xff09;二、CVE-2018-8715&#xff08;AppWeb认证绕过漏洞&#xff09;三、apisix3.1 CVE-2020-13945(默认密钥漏洞&#xff09;3.2 CVE-2021-45232&#xff08;Dashboard API权限绕过导致RCE&#xff09; 一…

只需一行代码提高3DGS重建质量!随机初始化新SOTA

论文标题&#xff1a; Relaxing Accurate Initialization Constraint for 3D Gaussian Splatting 论文作者&#xff1a; Jaewoo Jung, Jisang Han, Honggyu An, Jiwon Kang, Seonghoon Park and Seungryong Kim 导读&#xff1a; 3DGS在新视角合成和三维重建方面展现了令人印…

2022年CSP-J入门级第一轮初赛真题

一、单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1b;每题有且仅有一个正确选项&#xff09; 第 1 题 以下哪种功能没有涉及C语言的面向对象特性支持&#xff1a;&#xff08; &#xff09;。 A. C中调用printf函数 B. C中调用用户定义的类成…

使用cockpit管理服务器

安装cockpit yum install cockpit启用cockpit systemctl start cockpit浏览器中访问cockpit cockpit监听的端口是9090在浏览器中访问https://ip:9090/效果图 系统 日志 网络 账号

Git常用命令的使用

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

从cuda到cudnn到pytorch

一、预配版本信息 1、cuda12.1.1 2、cudnn8.9.7 3、pytorch2.2.0 二、引用 深度学习之环境配置&#xff1a;【CUDA 12.1.1cuDNN 8.9.1】最新安装教程记录 -- 20240429_torch 1.12.0对应torchvision-CSDN博客 补充&#xff1a; cuda历史版本索引&#xff1a; NVIDIA Dev…

零基础HTML教程(33)--HTML5表单新功能

文章目录 1. 背景2. HTML5新增表单元素2.1 number (数字输入框)2.2 email (邮箱输入框)2.3 url (链接输入框)2.4 tel (电话输入框)2.5 range (范围选择框)2.6 color (颜色选择框)2.7 datetime (日期时间选择框)2.8 search (搜索框) 3. placeholder &#xff08;占位属性&#x…