当前位置: 首页 > news >正文

C++封装红黑树实现mymap和myset和模拟实现详解

文章目录

  • map和set的封装
    • map和set的底层
  • map和set的模拟实现
    • insert
    • iterator实现的思路
    • operator++
    • operator- -
    • operator[ ]

map和set的封装

介绍map和set的底层实现

map和set的底层

一份模版实例化出key的rb_tree和pair<k,v>的rb_tree
rb_tree的Key和Value不是我们之前传统意义上的key/value
在这里插入图片描述
迭代器也是一份模版实例化出两个不同的迭代器
所以说底层上红黑树是有两份的,一份是key的,一份是key/value的
在这里插入图片描述

  1. 通过泛型的思想实现出的模版,第二个参数不是写死的,第二个模版参数是Value决定的,Value可以是key或者是pair<const key, T>,这样既可以实现key的搜索场景,也可以实现key/value的搜索场景
  2. 要注意一下,源码里面模板参数是用T代表value,而内部写的value_type不是我们我们日常key/value场景中说的value,源码中的value_type反而是红黑树结点中存储的真实的数据的类型
  3. rb_tree的第二个模版参数已经控制了红黑树节点的存储的数据类型,为什么还要写第二个模版参数?
    set的两个模版参数都是一样的,都是key,insert的都是key,find和erase的也是key。但是对于map来说,insert的是pair对象,find/erase的是key。所以set为了兼容map就传了两个模版参数

map和set的模拟实现

pair的默认比较的是first,first小就小,first不小,比较second,second小就小。

insert

insert关键是要解决插入的值是Key还pair的问题
上层用仿函数实现一个类来解决下层比较的是key还是pair的问题,下层不知道T是什么,但是上层知道,如果比较的是key上层就传key,是pair就传pair

namespace wbc
{template<class K>class set{// 为了解决不知道下层T是key还是pair的逻辑struct SetKeyOfT{// 仿函数可以解决const K& operator()(const K& key){return key;}};public:bool insert(const K& key){return _t.Insert(key);}private:RBTree<K,K,SetKeyOfT> _t;};
}namespace wbc
{template<class K, class V>class map{// 为了解决不知道下层T是key还是pair的逻辑struct MapKeyOfT{// 仿函数可以解决const pair<K, V>& operator()(const pair<K, V>& kv){return kv.first;}};private:RBTree<K, pair<K, V>,MapKeyOfT> _t;};
}

iterator实现的思路

  1. map和set的迭代器走的是中序遍历,begin()返回的是中序的第一个节点
  2. operator++核心是不看全局,只看局部,只关心当前局部的下一个节点是什么
  3. 中序访问的顺序:左子树,根,右子树。++,it当前节点已经访问完了,如果右不为空,访问右子树的最左节点。如果右为空,说明当前节点已经访问完了,子树也访问完了,就要访问该节点的祖先,并且要往上找。要找的是孩子是祖先的左边的那个祖先。
    如果孩子在父亲的右,说明父亲访问完了,父亲在爷爷的左,下一个就访问爷爷。
  4. set的iterator也不支持修改,我们把set的第二个模板参数改成const K即可, RBTree<K,const K, SetKeyOfT> _t;
  5. map的iterator不支持修改key但是可以修改value,我们把map的第二个模板参数pair的第一个参
    数改成const K即可, RBTree<K, pair<const K, V>,MapKeyOfT> _t;
    在这里插入图片描述

operator++

  1. 右不为空,中序的下一个节点就是右子树中的最左节点
  2. 右为空,分为两种情况:
    情况1:一直往上找直到找到父亲的左是当前节点,那么下一个节点就是这个父亲节点
    情况2:就是一直找,找不到父亲的左是当前节点,也就是当前节点一直是父亲的右,直到找到父亲是空都没有找到,那么这棵树就走完了
    在这里插入图片描述

在这里插入图片描述

Self operator++()
{if (_node->_right){// 如果右不为空,中序下一个要访问的节点就是最左(最小)节点Node* min = _node->_right;while (min->_left){min = min->_left;}_node = min;}else{// 如果右为空,祖先里面的孩子是父亲左的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_right){cur = parent;parent = cur->_parent;}_node = parent;}return *this;
}

operator- -

访问节点

  1. 右子树,根,左子树。如果左树不为空,访问左树的最右节点(最大节点)。如果左树为空,说明这棵子树访问完了,如果该节点还是父亲的左,说明也访问完了,要找到节点是父亲的右,下一个访问的节点就是父亲。如果都不存在,下一个就要访问空节点了,说明这棵树访问完了。
  2. operator- - 和operator++正好反过来了
    在这里插入图片描述
// RBTree.h
Self operator--()
{if (_node == nullptr) // --end(){// --end(),找到整棵树的最右节点,中序的最后一个节点// 找最右节点Node* mostright = _root;// 空树(无节点)和找最右节点while (mostright && mostright->_right){mostright = mostright->_right;}_node = mostright;}else if (_node->_left){// 如果左不为空,下一访问的是左树中的最右节点Node* most = _node->_left;while (most->_right){most = most->_right;}_node = most;}else{// 如果左为空,下一个访问的是孩子是父亲右的,那个祖先Node* cur = _node;Node* parent = _node->_parent;while (parent && cur == parent->_left){cur = parent;parent = cur->_parent;}_node = parent;}return *this;
}// Myset.h
void Print(const set<int>& s)
{set<int>::const_iterator it = s.end();while (it != s.begin()){--it;cout << *it << " ";}cout << endl;
}// test.cpp
int main()
{wbc::set<int> s;s.insert(5);s.insert(1);s.insert(2);s.insert(3);s.insert(4);s.insert(6);s.Print(s);
}

在这里插入图片描述
库里面实现的是有哨兵位的头节点,我们实现是end()是nullptr,但是也可以实现–end()的功能,无非就是要_root,去找整棵树的最右节点(最后一个节点),end()是最后一个节点的下一个节点
在这里插入图片描述

在这里插入图片描述

operator[ ]

operator[ ]直接调用insert即可
map实现operator[ ],修改insert的返回值为pair< Iterator,bool>

V& operator[](const K& key)
{pair<iterator, bool> ret = insert({ key,V() });return ret.first->second;// 修改value
}pair<Iterator,bool> Insert(const T& data)
{if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;// return pair<Iterator,bool>(Iterator(_root,_root),true);return { Iterator(_root,_root),true }; }KeyOfT kot;Node* parent = nullptr;Node* cur = _root;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 { Iterator(cur,_root),false }; ;}}cur = new Node(data);Node* newnode = cur;// 如果连续变色,cur不一定是新节点// 如果是非空树,插入红色节点cur->_col = RED;if (kot(parent->_data) < kot(data)){parent->_right = cur;}else if (kot(parent->_data) > kot(data)){parent->_left = cur;}// 链接父亲节点cur->_parent = parent;// parent是红色,出现了连续的红色节点,需要向上调整// 调整之后cur是根,cur的parent是nullptrwhile (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (grandfather->_left == parent){//   g// p     uNode* uncle = grandfather->_right;if (uncle && uncle->_col == RED){// 变色是为了处理连续的红节点,保证黑节点的数量不变,// 向上继续调整是因为grandfather的节点可能是黑节点就结束,// 可能是红节点就继续向上处理parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上处理cur = grandfather;parent = cur->_parent;}else{// uncle不存在或uncle存在且为黑//   g// p     u// c// 右单旋if (cur == parent->_left){RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{// 双旋//   g// p   u// cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else{//   g// u     pNode* uncle = grandfather->_left;if (uncle && uncle->_col == RED){parent->_col = uncle->_col = BLACK;grandfather->_col = RED;// 继续向上更新cur = grandfather;parent = cur->_parent;}else{// uncle不存在或者存在且是黑//    g// u     p//          c// 左单旋if (parent->_right == cur){RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//     g//      u     p//          c// 双旋RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}// 无论如何结束之后根都是黑色的_root->_col = BLACK;return {Iterator(newnode,_root),true};
}

相关文章:

C++封装红黑树实现mymap和myset和模拟实现详解

文章目录 map和set的封装map和set的底层 map和set的模拟实现insertiterator实现的思路operatoroperator- -operator[ ] map和set的封装 介绍map和set的底层实现 map和set的底层 一份模版实例化出key的rb_tree和pair<k,v>的rb_tree rb_tree的Key和Value不是我们之前传统意…...

二次封装的方法

二次封装 我们开发中经常需要封装一些第三方组件&#xff0c;那么父组件应该怎么传值&#xff0c;怎么调用封装好的组件原有的属性、插槽、方法&#xff0c;一个个调用虽然可行&#xff0c;但十分麻烦&#xff0c;我们一起来看更简便的方法。 二次封装组件&#xff0c;属性怎…...

消息队列篇--通信协议篇--网络通信模型(OSI7层参考模型,TCP/IP分层模型)

一、OSI参考模型&#xff08;Open Systems Interconnection Model&#xff09; OSI参考模型是一个用于描述和标准化网络通信功能的七层框架。它由国际标准化组织&#xff08;ISO&#xff09;提出&#xff0c;旨在为不同的网络设备和协议提供一个通用的语言和结构&#xff0c;以…...

Python实现U盘数据自动拷贝

功能&#xff1a;当电脑上有U盘插入时&#xff0c;自动复制U盘内的所有内容 主要特点&#xff1a; 1、使用PyQt5创建图形界面&#xff0c;但默认隐藏 2、通过CtrlAltU组合键可以显示/隐藏界面 3、自动添加到Windows启动项 4、监控USB设备插入 5、按修改时间排序复制文件 6、静…...

汇编的使用总结

一、汇编的组成 1、汇编指令&#xff08;指令集&#xff09; 数据处理指令: 数据搬移指令 数据移位指令 位运算指令 算术运算指令 比较指令 跳转指令 内存读写指令 状态寄存器传送指令 异常产生指令等 2、伪指令 不是汇编指令&#xff0c;但是可以起到指令的作用&#xff0c;伪…...

DeepSeek理解概率的能力

问题&#xff1a; 下一个问题是概率问题。乘车时有一个人带刀子的概率是百分之一&#xff0c;两个人同时带刀子的概率是万分之一。有人认为如果他乘车时带上刀子&#xff0c;那么还有其他人带刀子的概率就是万分之一&#xff0c;他乘车就会安全得多。他的想法对吗&#xff1f;…...

AI 浪潮席卷中国年,开启科技新春新纪元

在这博主提前祝大家蛇年快乐呀&#xff01;&#xff01;&#xff01; 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;其影响力已经渗透到社会生活的方方面面。在中国传统节日 —— 春节期间&#xff0c;AI 技术也展现出了巨大的潜力&#xff0c;为中国年带…...

AI时代的网络安全:传统技术的落寞与新机遇

AI时代的网络安全&#xff1a;传统技术的落寞与新机遇 在AI技术飞速发展的浪潮中&#xff0c;网络安全领域正经历着前所未有的变革。一方面&#xff0c;传统网络安全技术在面对新型攻击手段时逐渐显露出局限性&#xff1b;另一方面&#xff0c;AI为网络安全带来了新的机遇&…...

可以称之为“yyds”的物联网开源框架有哪几个?

有了物联网的发展&#xff0c;我们的生活似乎也变得更加“鲜活”、有趣、便捷&#xff0c;包具有科技感的。在物联网&#xff08;IoT&#xff09;领域中&#xff0c;也有许多优秀的开源框架支持设备连接、数据处理、云服务等&#xff0c;成为被用户们广泛认可的存在。以下给大家…...

线程局部存储tls的原理和使用

一、背景 tls即Thread Local Storage&#xff0c;也就是线程局部存储&#xff0c;可在进程内&#xff0c;多线程按照各个线程分开进行存储。对于一些与线程上下文相关的变量&#xff0c;可放到tls中&#xff0c;减少多线程之间的数据同步的开销。 有人可能会问&#xff0c;我…...

RK3588平台开发系列讲解(ARM篇)ARM64底层中断处理

文章目录 一、异常级别二、异常分类2.1、同步异常2.2、异步异常三、中断向量表沉淀、分享、成长,让自己和他人都能有所收获!😄 一、异常级别 ARM64处理器确实定义了4个异常级别(Exception Levels, EL),分别是EL0到EL3。这些级别用于管理处理器的特权级别和权限,级别越高…...

CAN总线

1. 数据帧&#xff08;Data Frame&#xff09; 数据帧是 CAN 总线中最常用的帧类型&#xff0c;用于传输实际的数据。其结构如下&#xff1a; 起始位&#xff08;Start of Frame, SOF&#xff09;&#xff1a;标志帧的开始。标识符&#xff08;Identifier&#xff09;&#x…...

qwen2.5-vl:阿里开源超强多模态大模型(包含使用方法、微调方法介绍)

1.简介 在 Qwen2-VL 发布后的五个月里&#xff0c;众多开发者基于该视觉语言模型开发了新的模型&#xff0c;并向 Qwen 团队提供了极具价值的反馈。在此期间&#xff0c;Qwen 团队始终致力于打造更具实用性的视觉语言模型。今天&#xff0c;Qwen 家族的最新成员——Qwen2.5-VL…...

python实现dbscan

python实现dbscan 原理 DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一个比较有代表性的基于密度的聚类算法。它将簇定义为密度相连的点的最大集合&#xff0c;能够把具有足够高密度的区域划分为簇&#xff0c;并可在噪声的空间数据库中发现任意形…...

学习数据结构(3)顺序表

1.动态顺序表的实现 &#xff08;1&#xff09;初始化 &#xff08;2&#xff09;扩容 &#xff08;3&#xff09;头部插入 &#xff08;4&#xff09;尾部插入 &#xff08;5&#xff09;头部删除 &#xff08;这里注意要保证有效数据个数不为0&#xff09; &#xff08;6&a…...

正在更新丨豆瓣电影详细数据的采集与可视化分析(scrapy+mysql+matplotlib+flask)

文章目录 豆瓣电影详细数据的采集与可视化分析(scrapy+mysql+matplotlib+flask)写在前面数据采集0.注意事项1.创建Scrapy项目`douban2025`2.用`PyCharm`打开项目3.创建爬虫脚本`douban.py`4.修改`items.py`的代码5.修改`pipelines.py`代码6.修改`settings.py`代码7.启动`doub…...

wx043基于springboot+vue+uniapp的智慧物流小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…...

每日一题 430. 扁平化多级双向链表

430. 扁平化多级双向链表 简单 /*class Solution { public:Node* flatten(Node* head) {Node* tail nullptr;return dfs(head);}Node* dfs(Node* head){Node* cur head;while(cur ! nullptr){if(cur->child ! nullptr){Node* curChild getTail(cur->child);Node* te…...

UE学习日志#14 GAS--ASC源码简要分析10 GC相关

注&#xff1a;1.这个分类是按照源码里的注释分类的 2.本篇是通读并给出一些注释形式的&#xff0c;并不涉及结构性的分析 3.看之前要对UE的GAS系统的定义有初步了解 4.因为都是接口函数&#xff0c;有些没细看的研究那一部分的时候会细看 1 一些接口函数&#xff0c;但是…...

使用Python和Qt6创建GUI应用程序--关于Qt的一点介绍

关于Qt的一点介绍 Qt是一个免费的开源部件工具包&#xff0c;用于创建跨平台GUI应用程序&#xff0c;允许应用程序从Windows瞄准多个平台&#xff0c;macOS&#xff0c; Linux和Android的单一代码库。但是Qt不仅仅是一个Widget工具箱和功能内置支持多媒体&#xff0c;数据库&am…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

如何应对敏捷转型中的团队阻力

应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中&#xff0c;明确沟通敏捷转型目的尤为关键&#xff0c;团队成员只有清晰理解转型背后的原因和利益&#xff0c;才能降低对变化的…...

深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向

在人工智能技术呈指数级发展的当下&#xff0c;大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性&#xff0c;吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型&#xff0c;成为释放其巨大潜力的关键所在&…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理

在城市的某个角落&#xff0c;一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延&#xff0c;滚滚浓烟弥漫开来&#xff0c;周围群众的生命财产安全受到严重威胁。就在这千钧一发之际&#xff0c;消防救援队伍迅速行动&#xff0c;而豪越科技消防一体化安全管控平台构建的消防“…...

uni-app学习笔记三十五--扩展组件的安装和使用

由于内置组件不能满足日常开发需要&#xff0c;uniapp官方也提供了众多的扩展组件供我们使用。由于不是内置组件&#xff0c;需要安装才能使用。 一、安装扩展插件 安装方法&#xff1a; 1.访问uniapp官方文档组件部分&#xff1a;组件使用的入门教程 | uni-app官网 点击左侧…...

动态规划-1035.不相交的线-力扣(LeetCode)

一、题目解析 光看题目要求和例图&#xff0c;感觉这题好麻烦&#xff0c;直线不能相交啊&#xff0c;每个数字只属于一条连线啊等等&#xff0c;但我们结合题目所给的信息和例图的内容&#xff0c;这不就是最长公共子序列吗&#xff1f;&#xff0c;我们把最长公共子序列连线起…...

Python的__call__ 方法

在 Python 中&#xff0c;__call__ 是一个特殊的魔术方法&#xff08;magic method&#xff09;&#xff0c;它允许一个类的实例像函数一样被调用。当你在一个对象后面加上 () 并执行时&#xff08;例如 obj()&#xff09;&#xff0c;Python 会自动调用该对象的 __call__ 方法…...