进阶数据结构:用红黑树实现封装map和set

嘿,各位技术潮人!好久不见甚是想念。生活就像一场奇妙冒险,而编程就是那把超酷的万能钥匙。此刻,阳光洒在键盘上,灵感在指尖跳跃,让我们抛开一切束缚,给平淡日子加点料,注入满满的
passion。准备好和我一起冲进代码的奇幻宇宙了吗?Let’s go!

请添加图片描述

我的博客:yuanManGan

我的专栏:C++入门小馆 C言雅韵集 数据结构漫游记 闲言碎语小记坊
进阶数据结构 走进Linux的世界 题山采玉 领略算法真谛

在这里插入图片描述


用红黑树实现封装map和set


实现步骤:

  1. 实现红黑树
  2. 封装 mapset 框架解决KeyOfT
  3. iterator
  4. const_iterator
  5. key不支持修改的问题
  6. operator[ ]

1.实现红黑树

上集我们已经实现了一个普通的红黑树 进阶数据结构:红黑树,


2.封装 map 和 set 框架解决KeyOfT


查看stl源码借鉴了解

发现实现 setmap 都包含了一个 tree 头文件,我们那篇博客实现的 key-value 的红黑树,那我们是不是要实现两个红黑树一个是 key 的一个 key-value 的呢?
stl中实现set和map所包含的头文件


不急我们看看源码:

template<typename _Key, typename _Val, typename _KeyOfValue,typename _Compare, typename _Alloc = allocator<_Val> >class _Rb_tree{}

我们凑凑这模板,这怎么又有key又有value那它怎么实现的set , set 不是不要value吗?


我来解释一下吧:

  • 可能写这个代码的程序员写的不太规范,这个地方的Val代表的不是我们之前写代码的value
  • 而是比如我们set传入的keymap传入的key_value,代表的是这一类,而不是单纯的value
  • 我们就这样形成了模板,本质上编译器还是写了两份代码,但增加了复用,减少了我们自己写。

那又有人问了,那我们不是只要一个模板参数就够了吗,为什么前面还得加个key呢?

  • 我们实现 find 等功能的时候需要使用到 key,所以不能偷懒

更改我们的红黑树

我们写就写的规范一点,我们将 V 替换为 T

//原本
////////////////////////////////////////////////////////////////
template <class K,class V>
struct RBTreeNode
{pair<K, V> _kv;RBTreeNode<K, V>* _parent;RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;Colour _col;RBTreeNode(const pair<K, V>& kv): _kv(kv), _parent(nullptr), _left(nullptr), _right(nullptr)  // 按声明顺序放在_col之前, _col(RED){}
};
template <class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public://...
private:Node* _root = nullptr;
};
////////////////////////////////////////////////////////////////
//修改后
template <class T>
struct RBTreeNode
{T _data;RBTreeNode<T>* _parent;RBTreeNode<V>* _left;RBTreeNode<V>* _right;Colour _col;RBTreeNode(T& data): _data(data), _parent(nullptr), _left(nullptr), _right(nullptr)  // 按声明顺序放在_col之前, _col(RED){}
};
template <class K, class T>
class RBTree
{typedef RBTreeNode<K, T> Node;
public://...
private:Node* _root = nullptr;
};

我们在修改 Insert 这个函数的时候就遇到的问题,我们怎么比较插入节点与原节点的大小呢?

  • 我们可以引入一个模板参数 KeyOfT 分别在 setmap头文件里面重载 ( ) 实现比较逻辑,如果是set就直接返回 keymap就返回kv中的 first

//set.h
template<class K>
class set
{struct SetKeyOfT{const K& operator()(const K& key){return key;}};
public://...
private:RBTree<K, K, SetKeyOfT> _t;	
};
//map.h
template<class K, class V>
class map
{struct MapKeyOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};
public://...
private:RBTree<K, pair<K, V>, MapKeyOfT> _t;	
};
  • insert
KeyOfT kot;
bool Insert(const T& data)
{//插入//空树if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (kot(data) > kot(cur->_data)){parent = cur;cur = cur->_right;}else if (kot(data) < kot(cur->_data)){parent = cur;cur = cur->_left;}else {return false;}}cur = new Node(data);cur->_col = RED;if (kot(data) > kot(parent->_data)){//是p的右子树parent->_right = cur;}else{//是p的左子树parent->_left = cur;}cur->_parent = parent;//变色while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;//u存在且为红if (uncle && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续往上处理cur = grandfather;parent = cur->_parent;}else{//u存在但为黑if (cur == parent->_left){//c在左//     g           p  //   p   u --->  c   g// c                   uRotateR(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{//c在右//     g    RotateL(p)        g    RotateR(g)    c//  p     u ----------->   c     u -------->  p     g//     c                 p                             u      RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{Node* uncle = grandfather->_left;//u存在且为红if (uncle && uncle->_col == RED){//变色parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续往上处理cur = grandfather;parent = cur->_parent;}else{//u存在但为黑if (cur == parent->_right){//c在右//     g           p  //   u   p --->  g   c//         c   uRotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{//c在z左//     g    RotateR(p)      g    RotateL(g)    c//  u     p -----------> u     c -------->  g     p//     c                        p        u      RotateR(parent);RotateL

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

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

相关文章

【数据结构初阶】--二叉树(五)

&#x1f525;个人主页&#xff1a;草莓熊Lotso &#x1f3ac;作者简介&#xff1a;C研发方向学习者 &#x1f4d6;个人专栏&#xff1a; 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》 ⭐️人生格言&#xff1a;生活是默默的坚持&#xff0c;毅力是永久的…

redis布隆过滤器解决缓存击穿问题

在电商系统中&#xff0c;商品详情页是一个典型的高频访问场景。当用户请求某个商品的详情时&#xff0c;系统会优先从缓存中获取数据。如果缓存中没有该商品的详情&#xff0c;系统会去数据库查询并更新缓存。然而&#xff0c;如果某个热门商品的缓存失效&#xff0c;大量请求…

1+1>2!特征融合如何让目标检测更懂 “场景”?

来gongzhonghao【图灵学术计算机论文辅导】&#xff0c;快速拿捏更多计算机SCI/CCF发文资讯&#xff5e;在多模态大模型&#xff08;MLLM&#xff09;时代&#xff0c;特征融合与目标检测的研究方向正变得愈发关键。从红外与可见光图像的融合&#xff0c;到语音活动检测中的特征…

详解赛灵思SRIO IP并提供一种FIFO封装SRIO的收发控制器仿真验证

概述RapidIO标准定义为三层&#xff1a;逻辑层、传输层、物理层。逻辑层&#xff1a;定义总体协议和包格式&#xff0c;包含设备发起/完成事务的必要信息。传输层&#xff1a;提供包传输的路由信息&#xff08;对顶层不可见&#xff09;。物理层&#xff1a;描述设备级接口细节…

深度学习:简介与任务分类总览

一、什么是深度学习&#xff1f;1.1 深度学习的定义深度学习&#xff08;Deep Learning&#xff09;是机器学习的一种特殊形式&#xff0c;它依赖于具有多层结构的神经网络自动从数据中学习特征并完成任务&#xff0c;如图像识别&#xff0c;语音识别&#xff0c;自然语言处理等…

MSPM0开发学习笔记:二维云台画图(2025电赛 附源代码及引脚配置)

前言 今年的电赛&#xff08;2025&#xff09;&#xff0c;很多题都与云台相关&#xff0c;因此为备战电赛&#xff0c;博主这边也是准备了一个由两个42步进电机驱动的云台并提前进行调试&#xff0c;避免赛题出来之后手忙脚乱的&#xff0c;这边的两个42步进电机采用同一个驱…

借助 Wisdom SSH 的 AI 助手构建 Linux 开发环境

借助Wisdom SSH的AI助手构建Linux开发环境 在Linux系统的开发场景中&#xff0c;快速、准确地搭建开发环境至关重要。Wisdom SSH凭借其强大的AI助手&#xff0c;能极大简化这一过程&#xff0c;其官网为ssh.wisdomheart.cn。以下以在Ubuntu 22.04服务器上构建Python开发环境&am…

Python 程序设计讲义(44):组合数据类型——集合类型:创建集合

Python 程序设计讲义&#xff08;44&#xff09;&#xff1a;组合数据类型——集合类型&#xff1a;创建集合 目录Python 程序设计讲义&#xff08;44&#xff09;&#xff1a;组合数据类型——集合类型&#xff1a;创建集合一、集合的特征二、创建集合&#xff1a;使用set()函…

10 - 大语言模型 —Transformer 搭骨架,BERT 装 “双筒镜”|解密双向理解的核心

目录 1、为什么 BERT 能 “懂” 语言&#xff1f;先看它的 “出身” 2、核心逻辑 2.1、“自学阶段”—— 预训练&#xff0c;像婴儿学说话一样积累语感 2.1.1、简述 2.1.2、核心本事&#xff1a;“双向注意力”&#xff0c;像人一样 “聚焦重点” 2.2、“专项复习”—— …

【Spring Boot 快速入门】四、MyBatis

目录MyBatis&#xff08;一&#xff09;入门简介MyBatis 入门LombokMyBatis 基础操作数据准备删除预编译新增更新查询XML 映射文件MyBatis&#xff08;一&#xff09;入门 简介 MyBatis 是一款 优秀的持久层框架&#xff0c;它支持 自定义 SQL、存储过程以及高级映射&#xf…

Spring IOC 基于Cglib实现含构造函数的类实例化策略

作者&#xff1a;小凯 分享、让自己和他人都能有所收获&#xff01; 一、前言 技术成长&#xff0c;是对场景设计细节不断的雕刻&#xff01; 你觉得自己的技术什么时候得到了快速的提高&#xff0c;是CRUD写的多了以后吗&#xff1f;想都不要想&#xff0c;绝对不可能&#xf…

composer 常用命令

### 设置镜像源全局设置composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/当个项目设置composer config repo.packagist composer https://mirrors.aliyun.com/composer/恢复官方源composer config -g --unset repos.packagist### 常用源阿里云…

【python】Python爬虫入门教程:使用requests库

Python爬虫入门教程&#xff1a;使用requests库 爬虫是数据获取的重要手段&#xff0c;下面我将通过一个完整的示例&#xff0c;教你如何使用Python的requests库编写一个简单的爬虫。我们将以爬取豆瓣电影Top250为例。 【python】网络爬虫教程 - 教你用python爬取豆瓣电影 Top…

OpenCV图像缩放:resize

图像缩放是图像处理中的基础操作之一。无论是图像预处理、数据增强还是图像金字塔构建&#xff0c;cv::resize 都是我们最常用的函数之一。但你是否注意到&#xff0c;在 OpenCV 中同时还存在一个名为 cv::Mat::resize 的方法&#xff1f;这两个函数虽然名字类似&#xff0c;但…

汽车、航空航天、适用工业虚拟装配解决方案

一、现状在制造业数字化转型浪潮中&#xff0c;传统装配过程仍面临诸多挑战&#xff1a;物理样机试错成本高、装配周期冗长、工艺优化依赖经验、跨部门协作效率低下……如何打破“试错-返工”的恶性循环&#xff1f;目前总装工艺通过DELMIA、NX、Creo等工程软件进行工艺装配验证…

页面跳转和前端路由的区别

传统方式&#xff1a;通过改变浏览器地址栏的 URL 来实现window.location.href /new-page<a href"/new-page">跳转到新页面</a>会导致整个页面重新加载会触发浏览器向服务器发送新的请求页面状态不会保留&#xff0c;所有资源重新加载可以避免新上线的内…

C/C++核心知识点详解

C/C核心知识点详解 1. 变量的声明与定义&#xff1a;内存分配的本质区别 核心概念 在C/C中&#xff0c;变量的声明和定义是两个完全不同的概念&#xff1a; 声明&#xff08;Declaration&#xff09;&#xff1a;告诉编译器变量的名称和类型&#xff0c;但不分配内存空间定义&a…

物联网发展:从概念到应用的演变历程

物联网的发展历程是一部技术革新与社会需求共同驱动的进化史&#xff0c;其演变可划分为概念萌芽、技术积累、应用拓展和智能融合四个阶段&#xff0c;每个阶段均以关键技术突破或社会需求变革为标志&#xff0c;最终形成万物互联的智能生态。以下是具体演变历程&#xff1a;一…

一个人开发一个App(数据库)

后端要保存数据&#xff0c;我还是选择了关系型数据库Mysql, 因为其它的不熟悉。 flutter端这次我选择的是ObjectBox&#xff0c;以前都是直接用的sqlite3&#xff0c;看对比ObjectBox效率比sqlite3高许多&#xff0c;这次前端为了用户体验&#xff0c;我需要缓存数据&#xff…

天铭科技×蓝卓 | “1+2+N”打造AI驱动的汽车零部件行业智能工厂

7月24日&#xff0c;杭州天铭科技股份有限公司&#xff08;简称 “天铭科技”&#xff09;与蓝卓数字科技有限公司&#xff08;简称 “蓝卓”&#xff09;签订全面战略合作协议。天铭科技董事长张松、副总经理艾鸿冰&#xff0c;蓝卓副董事长谭彰等领导出席签约仪式&#xff0c…