使用一份红黑树封装set和map

set&&map

  • set/map简介
    • set特性
    • map特性
    • 红黑树的接口
    • set的封装
    • map的封装
  • 总结

set/map简介

我们所熟知的vector、list、deque被称为序列是容器;set和map是c++中的关联式容器,他们都是用来储存数据的。
只不过关联式容器里面存储的不是单个元素,而是一个<key, value>键值对,这种键值对在数据检索时,效率更高。

键值对:用来表示具有一一对应关系的一种结构,该结构中只包含变量key和value,key代表键值,value表示与key对应的信息。比如英汉互译的词典<apple, 苹果> 就是一个键值对。

set特性

  • set是按照一定次序存储元素的容器。
  • 在set中,存储的是<key, key>这样的键值对,并且每个key必须是唯一的。set中的元素不能在容器中修改。但是可以从容器中,插入或删除他们
  • 在内部,set中的元素总是按照一定的顺序排列
  • set插入时,只需要插入value即可,不需要构造键值对
  • set底层使用红黑树实现

map特性

  • map是按照特定顺序存储的容器
  • map中存储的是<key, value>这样的键值对。并且每个key必须唯一。但是value值可以修改。也可以从容器中,插入或删除他们
  • 在内部,map是按照key的顺序排列的
  • map支持下标访问,且非常灵活。
  • map插入时,需要插入一个<key, value>键值对
  • map底层是用红黑树实现的

红黑树的接口

修改步骤:

  1. 将类模板修改为<K, T>, T为存入数据
  2. 增加一个仿函数的模板选项,用于获取数据的key
  3. 实现迭代器***
  4. 迭代器的单参构造(可以实现隐式转换)
  5. Insert函数,返回值的修改
  6. 迭代器++的逻辑
  7. 迭代器的typedef也要好好学学
enum class Color   //结点颜色{RED,BLACK};//1. 添加了T模板参数template<class T>struct RBTreeNode{//红黑树结点,默认红色RBTreeNode(const T& data):_left(nullptr),_right(nullptr),_parent(nullptr),_data(data),_color(Color::RED){}RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Color _color;};//3. 迭代器实现(数据,数据的引用,数据的指针)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){}// *、typedef __RBTreeIterator<T, T&, T*> itertaor;  拷贝构造// **、 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++()     //左根右{//1.右不为空,下一个就是右子树的最左结点if (_node->_right){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){Node* subRight = _node->_left;   //--的下一个是左子树的最右边while (subRight->_right){subRight = subRight->_right;}_node = subRight;}else{//左为空,找孩子是父亲的右的结点Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}};// 2. 注意添加了KeyOfT这个仿函数接口,用来把T里面的key给拿出来template<class K, class T, class KeyOfT>class RBTree{private:typedef RBTreeNode<T> Node;public://析构函数~RBTree(){_Destroy(_root);_root = nullptr;}public:typedef RBTreeIterator<T, T&, T*> iterator;typedef RBTreeIterator<T, const T&, const T*> const_iterator;iterator begin(){Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return iterator(cur);}iterator end(){return iterator(nullptr);}const_iterator begin() const{Node* cur = _root;while (cur && cur->_left){cur = cur->_left;}return const_iterator(cur);}const_iterator end() const{return const_iterator(nullptr);}public:Node* Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_data) < key){cur = cur->_right;}else if(kot(cur->_data) < key){cur = cur->_left;}else{return cur;}}return nullptr;}pair<iterator, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);_root->_color = Color::BLACK;   //根结点就置为黑色return make_pair(iterator(_root), true);}KeyOfT kot; //仿函数实例化//1.找到插入位置Node* cur = _root;Node* parent = nullptr;while (cur){if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else if(kot(cur->_data)>kot(data)){parent = cur;cur = cur->_left;}else{return make_pair(iterator(cur), false);}}//2.找到了直接插入Node* newNode = new Node(data);cur = newNode;if (kot(parent->_data) > kot(data)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;  //记得互联//3.检查是否满足红黑树,若满足直接退出//不满足,需要调整红黑树while(parent && parent->_color == Color::RED){//爷爷结点一定存在,因为parent为红,爷爷结点一定为黑!Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;//情况一: cur 为红,p为红,g为黑, u存在且为红if (uncle && uncle->_color == Color::RED)  // 只需要叔父爷变色即可{parent->_color = Color::BLACK;grandfather->_color = Color::RED;uncle->_color = Color::BLACK;        //子树调整完成//把爷爷当作新插入结点,继续调整cur = grandfather;parent = cur->_parent;}else   //2.u存在且为黑   3.u不存在    旋转+变色{//2    g//   p    u// cif (cur == parent->_left)   //父亲爷爷变色{_RotateR(grandfather);   //以g为轴点右单旋parent->_color = Color::BLACK;grandfather->_color = Color::RED;}else    //cur是p的右孩子{//    g//  p    u//    c       叔父爷变色_RotateL(parent);_RotateR(grandfather);grandfather->_color = Color::RED;cur->_color = Color::BLACK;}break;}}else   //parent = grandfather->_right{//   g// u    p//        cNode* uncle = grandfather->_left;if (uncle && uncle->_color == Color::RED)   //情况1  u存在且为红{uncle->_color = Color::BLACK;parent->_color = Color::BLACK;grandfather->_color = Color::RED;//继续往上调整cur = grandfather;parent = cur->_parent;}else   //u存在为黑    u不存在{//    g//  u   p//        cif (cur == parent->_right){//左单旋_RotateL(grandfather);parent->_color = Color::BLACK;grandfather->_color = Color::RED;}else{//    g//  u    p//      c_RotateR(parent);_RotateL(grandfather);grandfather->_color = Color::RED;cur->_color = Color::BLACK;}break;}}}_root->_color = Color::BLACK;return make_pair(iterator(newNode), true);}private:void _Destroy(Node* root){if (root == nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;}void _RotateR(Node* parent){//右单旋,儿子上位变父亲,父亲被挤到右边Node* pParent = parent->_parent;  //爷爷Node* childL = parent->_left;   //左孩子parent->_left = childL->_right;if (childL->_right){childL->_right->_parent = parent;   //子指向父}childL->_right = parent;parent->_parent = childL;  //子指向父//最后将chldL指向pParentchildL->_parent = pParent;if (pParent == nullptr)  // childL 变成了根{_root = childL;childL->_parent = nullptr;}else{if (pParent->_left == parent){pParent->_left = childL;}else{pParent->_right = childL;}}}//左单旋void _RotateL(Node* parent){Node* childR = parent->_right;   //存右孩子parent->_right = childR->_left;    //右孩子的左孩子互连父亲if (childR->_left){childR->_left->_parent = parent;}childR->_left = parent;   //parent和childR互联Node* pparent = parent->_parent;   //暂存爷爷parent->_parent = childR;//爷爷和childR互联childR->_parent = pparent;if (pparent == nullptr)   //爷爷为空,就是根{_root = childR;childR->_parent = nullptr;}else{if (pparent->_left == parent){pparent->_left = childR;}else{pparent->_right = childR;}}}private:Node* _root=nullptr;   //根};

set的封装

注意问题:

  1. 要写一个仿函数,只有自己知道自己的数据类型
  2. 因为set不可更改value,所以要使用const迭代器,注意typedef的都是const命名的。
  3. 封装的成员变量直接是红黑树。
	template<class K>class set{//我自己直到数据类型是什么样的,仿函数我来写struct SetKeyOfT{const K& operator()(const K& key){return key;}};public: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();}public:pair<iterator, bool> Insert(const K& key){return _t.Insert(key);}private:RBTree<K, K, SetKeyOfT> _t;    //定义为红黑树};

map的封装

封装步骤:

  1. 模板参数不需要加仿函数!!
  2. 定义仿函数时,注意此参数设置应该具体
  3. 迭代器只需实现一份即可
  4. 注意[]的实现注意事项
	//template<class K, class T, class KeyOfT>   自己写的,写的不好template<class K, class V>class map{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}V& operator[](const K& key){pair<iterator, bool> ret = _t.Insert(make_pair(key, V()));return ret.first->second;}pair<iterator, bool> insert(const pair<const K, V>& kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V>, MapKeyOfT> _t;};

总结

map和set的封装,重点是封装的思想。多学学这里代码的套路。

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

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

相关文章

CentOS Linux操作系统源码安装最新Redis版本,使用JSON数据类型踩入新坑

最近有空查阅了redis官网&#xff0c;发现redis数据类型不止Strings、Lists、Sets、Hashes、Sorted sets&#xff0c;还多了几种&#xff0c;决定先试用下JSON数据类型 1、安装Redis软件 JSON数据类型&#xff0c;对Redis版本有要求&#xff0c;需要大于4.0版本。下图是华为云…

开源项目介绍

浙大高飞课题组 微分平坦 微分平坦的思想是&#xff1a;一个全维度的状态空间可以被一组低维的精心挑选的输出平坦空间&#xff08;flat-output space&#xff09;的变量及其导数的代数组合的方式所表示。由此&#xff0c;轨迹规划就可以在这组精心挑选的变量的空间所进行。 …

【C++提高编程(二)】

一、STL初识 1.1、STL的诞生 长久以来&#xff0c;软件界一直希望建立一种可重复利用的东西 C的面向对象和泛型编程思想&#xff0c;目的就是复用性的提升 大多情况下&#xff0c;数据结构和算法都未能有一套标准,导致被迫从事大量重复工作 为了建立数据结构和算法的一套标…

一文详解 Berachain 测试网:全面介绍与教程,bitget wallet教程

什么是Berachain&#xff1f; Berachain&#xff08;web3.bitget.com/zh-CN/assets/berachain-wallet&#xff09;是一种尖端区块链技术&#xff0c;使用 Cosmos SDK 构建的 Layer-1&#xff0c;兼容以太坊虚拟机&#xff08;EVM&#xff09;。它基于一种独特的概念&#xff0c…

【AI】人工智能和图像编码(2)

传统图像编解码与智能图像编解码&#xff0c;都是要编码和解码&#xff0c;但还是有一些区别的。 相关相同点和要点描述如下&#xff1a; 一、区别 1.1 技术原理 传统图像编解码&#xff1a;主要依赖于固定的算法和标准&#xff0c;如JPEG、MPEG等&#xff0c;进行图像的压…

[晓理紫]每日论文分享(有中文摘要,源码或项目地址)--机器人相关、强化学习

专属领域论文订阅 VX 扫吗关注{晓理紫|小李子}&#xff0c;每日更新论文&#xff0c;如感兴趣&#xff0c;请转发给有需要的同学&#xff0c;谢谢支持 分类: 大语言模型LLM视觉模型VLM扩散模型视觉导航具身智能&#xff0c;机器人强化学习开放词汇&#xff0c;检测分割 [晓理紫…

LeetCode、2542. 最大子序列的分数【中等,排序+小顶堆】

文章目录 前言LeetCode、2542. 最大子序列的分数【中等&#xff0c;排序小顶堆】题目及类型思路及代码实现 资料获取 前言 博主介绍&#xff1a;✌目前全网粉丝2W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领…

2024最新AWVS/Acunetix Premium v24.1.24高级版漏洞扫描器Windows/Linux下载

前言 Acunetix Premium 是一种 Web 应用程序安全解决方案&#xff0c;用于管理多个网站、Web 应用程序和 API 的安全。集成功能允许您自动化 DevOps 和问题管理基础架构。 Acunetix Premium&#xff1a;全面的 Web 应用程序安全解决方案 Web 应用程序对于企业和组织与客户、合作…

多选下拉框(select 下拉多选)

https://www.cnblogs.com/sherryweb/p/11057746.html 多选下拉框&#xff08;select 下拉多选&#xff09; 方法一&#xff1a;使用multiple-select.js和multiple-select .css实现 HTML代码&#xff1a; <select idcheckedLevel style"width:120px;height:28px;"…

算法练习-替换数字(思路+流程图+代码)

难度参考 难度&#xff1a;简单 分类&#xff1a;字符串 难度与分类由我所参与的培训课程提供&#xff0c;但需要注意的是&#xff0c;难度与分类仅供参考。以下内容均为个人笔记&#xff0c;旨在督促自己认真学习。 题目 给定一个字符串S,它包含小写字母和数字字符&#xff0…

EXCEL VBA获取幸运数字号码

EXCELVBA获取幸运数字号码 以下就是VBA幸运号码产生的程序&#xff0c;复制粘贴到VBA代码框即可运行 Option Base 1 Sub 幸运号码()Dim n As Integer, i As Integer, j As IntegerDim l() As Integern Application.InputBox("请输入需要产生幸运号码的数量&#xff1a;…

设备巡检系统开发及部署

**凡尔码设备巡检系统**是一种低代码模块搭建设备管理系统平台&#xff1b;用户可通过平台开发好的组件像搭积木一般灵活搭建设备管理平台和无纸化应用场景。凡尔码平台功能组件&#xff1a;二维码管理、表单管理、流程管理、计划管理、权限管理、隐患管理、区域管理、记录管理…

Pytorch数据操作

数据操作 # 导入PyTorch import torch [张量表示一个由数值组成的数组&#xff0c;这个数组可能有多个维度]。 具有一个轴的张量对应数学上的向量&#xff08; &#xff09;&#xff1b; 具有两个轴的张量对应数学上的矩阵&#xff08;matrix&#xff09;&#xff1b; 具有两…

docker下安装rabbitmq

1.查询rabbitmq的镜像 docker search rabbitmq 2.安装镜像 如果需要安装其他版本在rabbitmq后面跟上版本号即可 docker pull rabbitmq:3.7.7-management docker pull rabbitmq:版本号 -management 直接安装最新的 docker pull rabbitmq 3.启动容器 docker run -dit --name rab…

数据结构之二叉树的性质与存储结构

数据结构之二叉树的性质与存储结构 1、二叉树的性质2、二叉树的存储结构 数据结构是程序设计的重要基础&#xff0c;它所讨论的内容和技术对从事软件项目的开发有重要作用。学习数据结构要达到的目标是学会从问题出发&#xff0c;分析和研究计算机加工的数据的特性&#xff0c;…

【趣味题-03】20240120猴子吃桃( 从大到小insert ,列表元素互减)

背景需求&#xff1a; 猴子摘桃的题目 解决&#xff1a; 猴子吃桃 倍数问题 作者&#xff1a;阿夏 时间&#xff1a;2024年1月20日猴子吃桃问题-1 猴子第一天摘了许多桃子&#xff0c;第一天吃了一半&#xff0c;&#xff1b;第二天又吃了一半&#xff0c; 后面每天都是这样吃…

【Java】HttpServlet类简单方法和请求显示

1、HttpServlet类简介&#x1f340; Servlet类中常见的三个类有&#xff1a;☑️HttpServlet类&#xff0c;☑️HttpServletRequest类&#xff0c;☑️HttpResponse类 &#x1f42c;其中&#xff0c;HttpServlet首先必须读取Http请求的内容。Servlet容器负责创建HttpServlet对…

Deepin_Ubuntu_查看树形目录结构(tree)

Linux系统&#xff08;Deepin、Ubuntu&#xff09;中&#xff0c;可以使用tree命令来查看树形目录结构&#xff0c;下面是一些示例&#xff1a; 查看当前目录的树形结构&#xff1a; tree查看指定目录的树形结构&#xff0c;例如/etc/X11/fonts目录&#xff1a; tree /etc/X…

CentOS 7安装Java并配置环境

一、安装Java环境 1、检查系统是否安装Java [rootlocalhost ~]# java -version 2、更新系统软件包 [rootlocalhost ~]# yum update #遇到[y/n],选择y并回车&#xff0c;耐心等待下载完毕&#xff0c;之后系统会自动检验更新的软件包遇到 /var/run/yum.pid 已被锁定 /var/…

SQL Server 恢复软件

Datanumen SQL Server 软件主要特点 支持 Microsoft SQL Server 2005、2008、2008 R2、2012、2014、2016、2017、2019、2022 。 恢复表中的架构/结构和数据。 恢复所有数据类型&#xff0c;包括 ASCII 和 Unicode XML 数据类型。 恢复稀疏列。 恢复数据库表中已删除的记录…