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

用红黑树封装实现map和set

目录

1、map和set的底层

2、map与set中的key关键值

3、红黑树迭代器的实现。

1、++操作

2、-- 操作

3、==和!=操作

4、在红黑树中封装迭代器

5、map和set对迭代器的封装

1、map

map中[]的重载

2、set


1、map和set的底层

        map和set都是基于红黑树实现的。红黑树是一种自平衡的二叉搜索树,它保持着良好的平衡性能,插入、删除、查找的时间复杂度都是O(log n)。在C++ STL中,map和set都是使用红黑树作为底层数据结构来实现的。红黑树在前面文章模拟实现过,可以参考

rep_type通常是用来表示set容器内部红黑树的节点类型。在STL头文件中,通常会定义rep_type作为红黑树节点的类型,用于表示set容器和map容器内部的数据结构。 

可以看到set和map的源代码中都使用了红黑树作为底层数据结构。

可以看到红黑树中的数据类型只有一个,

而在map和set中都有key值,也有数据类型,map的数据类型是键值对pair<const key,T>,set中的数据类型也是key。

2、map与set中的key关键值

那么我们该如何模拟实现呢?

🚀map传给红黑树的模板参数是pair键值对

🚀set传给红黑树的模板参数是key值

 红黑树该如何判断key值来比较大小呢,这个时候我们需要使用仿函数的方法,因为红黑树比较节点只看key值。我们选择仿函数的方法:

map.h文件:

template<class K,class V>
class MYmap
{struct KeyofMap{const K& operator ()(const pair<K,V> &kv){return kv.first;}};
private:RBTree<K, pair<const K, V>, KeyofMap> _t;
};

set.h文件:

template<class T>
class Myset
{struct SetKeyofvalue{const T& operator()(const T& key){return key;}};
private:RBTree<T, const T, SetKeyofvalue> _t;
};

set存放的数据本身就是key,但是为了与红黑树结构:

以及map的结构统一,所以也需要实现一个。

3、红黑树迭代器的实现。

1、++操作

++操作也就是找到下一个比它大的值,那么就有两种可能:

1.右子树的最小值。

//如果右子树不为空,那么比这个元素大的下一个节点就是右子树的最小元素

//也就是右子树的最左节点

2.如果没有右子树,那么向上查找,直到找到一个节点是其父节点的左字数,该父节点就是符合条件的值。

Self& operator++(){//如果右子树不为空,那么比这个元素大的下一个节点就是右子树的最小元素//也就是右子树的最左节点if (_node->_right){Node* subrm = _node->_right;while (subrm->_left){subrm = subrm->_left;}_node = subrm;}//如果右子树为空,那么向上查找,直到找到一个节点是其父节点的左子树else{Node* cur = _node;Node* parent = cur->_parent;while (parent&& parent->_right == cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}

2、-- 操作

--操作也就是找到下一个比它小的值,那么就有两种可能:

1.左子树存在

//找出这个迭代器前面一个比它小的元素,如果左子树存在,那么就先找左边最大的节点
//也就是右子树的最右节点

2.左子树不存在

 //如果左子树为空,那么向上查找,直到找到一个节点是其父节点的右子树

Self& operator --(){//找出这个迭代器前面一个比它小的元素,如果左子树存在,那么就先找左边最大的节点//也就是右子树的最右节点if (_node->_left){Node* sublmax = _node->_left;while (sublmax->_right){sublmax = sublmax->_right;}_node = sublmax;}//如果左子树为空,那么向上查找,直到找到一个节点是其父节点的右子树else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}

3、==和!=操作

//重载!=运算符bool operator!=(const Self& s){return _node != s._node;}//重载==运算符bool operator == (const Self & s){return _node == s._node;}

比较的内容是迭代器指向的节点是不是相同,不是比较节点的key值,同时也不需要修改,所以这里只需要const引用即可。

红黑树迭代器完整实现代码:


//红黑树迭代器
template<class T>
struct __RBTreeIterator
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T> Self;Node* _node;__RBTreeIterator(Node* node):_node(node){}T& operator *(){return _node->_data;}T* operator ->(){return &_node->_data;}Self& operator++(){//如果右子树不为空,那么比这个元素大的下一个节点就是右子树的最小元素//也就是右子树的最左节点if (_node->_right){Node* subrm = _node->_right;while (subrm->_left){subrm = subrm->_left;}_node = subrm;}//如果右子树为空,那么向上查找,直到找到一个节点是其父节点的左子树else{Node* cur = _node;Node* parent = cur->_parent;while (parent&& parent->_right == cur){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}Self& operator --(){//找出这个迭代器前面一个比它小的元素,如果左子树存在,那么就先找左边最大的节点//也就是右子树的最右节点if (_node->_left){Node* sublmax = _node->_left;while (sublmax->_right){sublmax = sublmax->_right;}_node = sublmax;}//如果左子树为空,那么向上查找,直到找到一个节点是其父节点的右子树else{Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->_left){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}//重载!=运算符bool operator!=(const Self& s){return _node != s._node;}//重载==运算符bool operator == (const Self & s){return _node == s._node;}};

4、在红黑树中封装迭代器

在红黑树中,起始节点begin()是最左节点

结束位置是空位置节点

本文采用上面的实现方法。

为了实现左闭右开,STL库采用的是end指向哨兵位头结点,最左边节点也和头相连。


template<class K,class T,class KeyofT>
class RBTree
{
public:typedef __RBTreeIterator<T> iterator;typedef RBTreeNode<T> Node;KeyofT kot;iterator begin(){Node* subleft = _root;while (subleft && subleft->_left){subleft = subleft->_left;}return iterator(subleft);}iterator end(){return iterator(nullptr);}
private:Node* _root=nullptr;
};

5、map和set对迭代器的封装

1、map

typedef typename RBTree<K, pair<const K, V>, KeyofMap>::iterator iterator;

这里要使用typename

当在模板中使用嵌套类型或依赖于模板参数的类型推导时,需要使用typename关键字来告诉编译器这是一个类型而不是一个变量。这通常出现在模板中使用嵌套类型或依赖于模板参数的类型推导时。

map中[]的重载

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

 map中的[]可以实现查找,修改,插入三个功能。

当使用[]操作符访问map中的元素时,

如果该键已经存在,则返回对应的值;

如果该键不存在,则会插入一个新的键值对,并返回一个默认构造的值。

在红黑树中也要对insert进行修改来匹配 

 

我们来测试一下修改(更改value值):

#pragma once
#include"RBTree.h"template<class K,class V>
class MYmap
{struct KeyofMap{const K& operator ()(const pair<K,V> &kv){return kv.first;}};
public:typedef typename RBTree<K, pair<const K, V>, KeyofMap>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}V& operator[](const K& key){pair<iterator, bool> ret = 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>, KeyofMap> _t;
};

2、set

#pragma once
#include"RBTree.h"
template<class T>
class Myset
{struct SetKeyofvalue{const T& operator()(const T& key){return key;}};public:typedef typename RBTree<T,const T, SetKeyofvalue>::iterator iterator;iterator begin(){return _t.begin();}iterator end(){return _t.end();}pair<iterator,bool> Insert(const T& key){return _t.Insert(key);}private:RBTree<T, const T, SetKeyofvalue> _t;
};

在C++的std::mapstd::set容器中,元素的键值是不可更改的,这是因为这两个容器是基于红黑树实现的,红黑树的性质要求元素的键值在插入后不能被修改,否则会破坏红黑树的结构。

所以在实现时,map使用了

RBTree<K, pair<const K, V>, KeyofMap> _t;

set使用了

RBTree<T, const T, SetKeyofvalue> _t; 

来保证key值不会被修改。 

下面给出红黑树完整的插入函数:

pair<iterator,bool> Insert(const  T& data){if (_root == nullptr){_root = new Node(data, BLACK);Node* newnode = _root;//根的双亲为头结点return make_pair(iterator(newnode), true);}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 make_pair(cur,false);}}cur = new Node(data, RED);Node* newnode = cur;if (kot(cur->_data) < kot(parent->_data)){parent->_left = cur;}else{parent->_right = cur;}cur->_parent = parent;while (parent && parent->_color == RED){Node* grandparent = parent->_parent;if (parent == grandparent->_left){Node* uncle = grandparent->_right;if (uncle && uncle->_color == RED){grandparent->_color = RED;parent->_color = BLACK;uncle->_color = BLACK;cur = grandparent;parent = cur->_parent;}else{if (cur == parent->_left){RotateR(grandparent);parent->_color = BLACK;grandparent->_color = RED;}else{RotateL(parent);RotateR(grandparent);cur->_color = BLACK;grandparent->_color = RED;}break;}}else{Node* uncle = grandparent->_left;if (uncle && uncle->_color == RED){grandparent->_color = RED;parent->_color = BLACK;uncle->_color = BLACK;cur = grandparent;parent = cur->_parent;}else{if (cur == parent->_right){RotateL(grandparent);parent->_color = BLACK;grandparent->_color = RED;}else{RotateR(parent);RotateL(grandparent);cur->_color = BLACK;grandparent->_color = RED;}break;}}}_root->_color = BLACK;return make_pair(iterator(newnode),true);}

通过封装实现MapSet,可以使用红黑树的特性来实现键值对的存储和查找,同时保持数据结构的平衡性和高效性。以上就是用红黑树封装实现map和set,当然这只是简单实现,对于更好地理解和使用这个容器比较有帮助。

相关文章:

用红黑树封装实现map和set

目录 1、map和set的底层 2、map与set中的key关键值 3、红黑树迭代器的实现。 1、操作 2、-- 操作 3、和!操作 4、在红黑树中封装迭代器 5、map和set对迭代器的封装 1、map map中[]的重载 2、set 1、map和set的底层 map和set都是基于红黑树实现的。红黑树是一种自平衡…...

【阿里云系列】-部署ACK集群的POD应用日志如何集成到日志服务(SLS)中

介绍 我们在实际部署应用到阿里云的ACK集群后&#xff0c;由于后期应用服务的持续维护诉求可能需要跟踪排查问题&#xff0c;此时就要具备将应用的历史日志存档便于后期排查问题 处理方式 为了解决以上的普遍需求&#xff0c;需要将ACK中的应用日志采集到SLS的Logstore中,然…...

Vue中给当前页面传递参数并重新加载,vue使用this.$router.push跳转页面,给跳转过去的页面传参不一致时重新加载

vue通过this.$router.push给url传参&#xff0c;改变url但是当前页面不会自动刷新 跳转页面代码 this.$router.push({name: allbusiness,query: {pw_id: item.id} });1.使用 watch 监听 $route 对象的变化&#xff0c;当路由发生变化时重新加载数据或执行其他操作。 2.利用路…...

【扩散模型(一)】综述:扩散模型在文本生成领域应用

一、论文信息 1 标题 Diffusion models in text generation: a survey 2 作者 Qiuhua Yi, Xiangfan Chen, Chenwei Zhang, Zehai Zhou, Linan Zhu, Xiangjie Kong 3 研究机构 1 College of Computer Science and Technology, Zhejiang University of Technology, HangZho…...

K8S Pod

基本概念 Pod是K8S中非常重要的概念之一&#xff0c;是整个K8S架构的基础和核心。Pod是K8S调度的最小单位&#xff0c;是一个不可拆分的独立个体&#xff0c;K8S将多个业务上相关联的容器&#xff08;Docker容器&#xff09;合并到一起&#xff0c;组合成一个Pod&#xff0c;这…...

反向传播(backward propagation,BP) python实现

BP算法就是反向传播&#xff0c;要输入的数据经过一个前向传播会得到一个输出&#xff0c;但是由于权重的原因&#xff0c;所以其输出会和你想要的输出有差距&#xff0c;这个时候就需要进行反向传播&#xff0c;利用梯度下降&#xff0c;对所有的权重进行更新&#xff0c;这样…...

简单算命脚本

效果展示 文件内容 main.py文件 import json import random import time# 别挂配置数据 gua_data_path "data.json"# 别卦数据 gua_data_map {} fake_delay 10# 读取别卦数据 def init_gua_data(json_path):with open(gua_data_path, r, encodingutf8) as fp:gl…...

Lua-掌握Lua语言基础1

Lua是一种轻量级的脚本语言&#xff0c;广泛应用于游戏开发、嵌入式系统和其他领域。下面是Lua语言基础的介绍&#xff1a; 数据类型&#xff1a;Lua支持基本的数据类型&#xff0c;包括nil、boolean、number、string和table。其中&#xff0c;table是一种关联数组&#xff0c;…...

python-0003-pycharm开发虚拟环境中的项目

前言 在虚拟环境中创建好了python项目&#xff0c;使用pycharm进行开发 打开项目 使用pycharm打开项目 设置虚拟环境的解释器 File–>Settings–>Project(项目名)–>Python Interpreter–>添加解释器–>添加已经存在的解释器–>选择虚拟环境的解释器 …...

修改 MySQL update_time 默认值的坑

由于按规范需要对 update_time 字段需要对它做默认值的设置 现在有一个原始的表是这样的 CREATE TABLE test_up (id bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 主键id,update_time datetime default null COMMENT 操作时间,PRIMARY KEY (id) ) ENGINEInnoDB DEF…...

基于亚马逊云EC2+Docker搭建nextcloud私有化云盘

亚马逊云科技EC2云服务器&#xff08;Elastic Compute Cloud&#xff09;是亚马逊云科技AWS&#xff08;Amazon Web Services&#xff09;提供的一种云计算服务。EC2代表弹性计算云&#xff0c;它允许用户租用虚拟计算资源&#xff0c;包括CPU、内存、存储和网络带宽&#xff0…...

用try...catch进行判断

在写一些提交数据的判断上&#xff0c;有时候会写下面的ifelse的判断方法&#xff0c;少一点还好&#xff0c;多的话就很难受也不好看。 if(!that.driverObj.contrary){this.__utils.showToast(请先上传驾驶证副页图片);return false } if(!this.driverObj.start){this.__util…...

服务器遭遇挖矿病毒syst3md及其伪装者rcu-sched:原因、症状与解决方案

01 什么是挖矿病毒 挖矿病毒通常是恶意软件的一种&#xff0c;它会在受感染的系统上无授权地挖掘加密货币。关于"syst3md"&#xff0c;是一种特定的挖矿病毒&#xff0c;它通过在受感染的Linux系统中执行一系列复杂操作来达到其目的。这些操作包括使用curl从网络下载…...

此机非彼机,工业计算机在工业行业的特殊地位

电子计算机&#xff0c;称为电脑。计算机是一种利用数字电子技术&#xff0c;根据一系列指令指示其自动执行任意算术或逻辑操作串行的设备。通用计算机因有能遵循被称为“程序”的一般操作集的能力而使得它们能够执行极其广泛的任务&#xff0c;以管理各种工厂和机器自动化工业…...

Python使用lxml解析XML格式化数据

Python使用lxml解析XML格式化数据 1. 效果图2. 源代码参考 方法一&#xff1a;无脑读取文件&#xff0c;遇到有关键词的行再去解析获取值 方法二&#xff1a;利用lxml等库&#xff0c;解析格式化数据&#xff0c;批量获取标签及其值 这篇博客介绍第2种办法&#xff0c;以菜鸟教…...

CDA-LevelⅡ【考题整理-带答案】

关于相关分析中应注意的问题&#xff0c;下面说法错误的是&#xff1a;B 如果两变量间的相关系数为0&#xff0c;则说明二者独立 。解释&#xff1a;只能说明两者不存在线性相关关系现通过参数估计得到一个一元线性回归模型为y3x4&#xff0c;在回归系数检验中下列说法错误的是…...

20240304 json可以包含复杂数组(数组里面套数组)

欣赏一下我的思维&#xff0c;它会以漫画&#xff0c;表格&#xff0c;文字。。。各种各样的形式呈现 对于问题1问题2 JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式&#xff0c;易于人阅读和编写&#xff0c;同时也易于机器解析和生成。JSON本质上是一种文本…...

算法50:动态规划专练(力扣514题:自由之路-----4种写法)

题目: 力扣514 &#xff1a; 自由之路 . - 力扣&#xff08;LeetCode&#xff09; 题目的详细描述&#xff0c;直接打开力扣看就是了&#xff0c;下面说一下我对题目的理解: 事例1&#xff1a; 输入: ring "godding", key "gd" 输出: 4. 1. ring的第…...

重学SpringBoot3-集成Thymeleaf

更多SpringBoot3内容请关注我的专栏&#xff1a;《SpringBoot3》 期待您的点赞&#x1f44d;收藏⭐评论✍ 重学SpringBoot3-集成Thymeleaf 1. 添加Thymeleaf依赖2. 配置Thymeleaf属性&#xff08;可选&#xff09;3. 创建Thymeleaf模板4. 创建一个Controller5. 运行应用并访问页…...

【数据可视化】Echarts最常用图表

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 前言2. 准备工作3. 柱状图3.1 绘制堆积柱状图3.2 绘制标准条形图3.3 绘制瀑布图 4. 折线图4.1 绘制堆积面积图和堆积折线图4.2 绘制阶梯图 5. 饼图5.1 绘制标准饼图5.2 绘制圆环图5.2 绘制嵌套饼图5.3 绘制南丁格尔…...

HTML 语义化

目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案&#xff1a; 语义化标签&#xff1a; <header>&#xff1a;页头<nav>&#xff1a;导航<main>&#xff1a;主要内容<article>&#x…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例

文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

Rust 开发环境搭建

环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行&#xff1a; rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu ​ 2、Hello World fn main() { println…...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1&#xff1a;通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分&#xff0c;设置 Gradle JDK 方法2&#xff1a;通过 Settings File → Settings... (或 CtrlAltS)…...

Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解

文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一&#xff1a;HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二&#xff1a;Floyd 快慢指针法&#xff08;…...

GeoServer发布PostgreSQL图层后WFS查询无主键字段

在使用 GeoServer&#xff08;版本 2.22.2&#xff09; 发布 PostgreSQL&#xff08;PostGIS&#xff09;中的表为地图服务时&#xff0c;常常会遇到一个小问题&#xff1a; WFS 查询中&#xff0c;主键字段&#xff08;如 id&#xff09;莫名其妙地消失了&#xff01; 即使你在…...