C++ STL :红黑树rb_tree源码剖析

STL关联式容器map、set、multimap、multiset,绝大部分操作如插入、修改、删除、搜索,都是由其内含的红黑树来完成的。

红黑树数据结构和算法的讲解见:

数据结构与算法:红黑树讲解-CSDN博客

我下面会总结  STL中rb_tree怎么实现的。

首先,rb_tree是红黑树,所以需要定义红色和黑色。

enum _Rb_tree_color { _S_red = false, _S_black = true };//红黑树的颜色 红色0 黑色1

然后需要定义 红黑树的节点。

struct _Rb_tree_node_base{typedef _Rb_tree_node_base* _Base_ptr; //节点指针typedef const _Rb_tree_node_base* _Const_Base_ptr;//const节点指针_Rb_tree_color    _M_color;//颜色_Base_ptr        _M_parent;//父节点_Base_ptr        _M_left;//左节点_Base_ptr        _M_right;//右节点static _Base_ptr//最小节点,即最左节点_S_minimum(_Base_ptr __x) _GLIBCXX_NOEXCEPT{while (__x->_M_left != 0) __x = __x->_M_left;//只要左节点不为空就一直向左走,取得最小节点return __x;}static _Const_Base_ptr_S_minimum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT{while (__x->_M_left != 0) __x = __x->_M_left;return __x;}static _Base_ptr//最大节点,即最右节点_S_maximum(_Base_ptr __x) _GLIBCXX_NOEXCEPT{while (__x->_M_right != 0) __x = __x->_M_right;return __x;}static _Const_Base_ptr_S_maximum(_Const_Base_ptr __x) _GLIBCXX_NOEXCEPT{while (__x->_M_right != 0) __x = __x->_M_right;return __x;}};

  _Rb_tree_node_base定义了红黑树的节点类,从类中可以看出一个节点有颜色、父指针、左孩子指针、右孩子指针4个属性。然后定义了几个函数,可以找到以这个节点为根节点的红黑树的最大节点和最小节点。

   template<typename _Val>//红黑树的节点结构struct _Rb_tree_node : public _Rb_tree_node_base{typedef _Rb_tree_node<_Val>* _Link_type;//节点指针 指向数据节点#if __cplusplus < 201103L_Val _M_value_field;//数据类型_Val*_M_valptr(){ return std::__addressof(_M_value_field); } const _Val*_M_valptr() const{ return std::__addressof(_M_value_field); }
#else__gnu_cxx::__aligned_buffer<_Val> _M_storage;//对齐处理后数据_Val*_M_valptr() //返回对应数据的指针{ return _M_storage._M_ptr(); }const _Val*_M_valptr() const{ return _M_storage._M_ptr(); }
#endif};

 _Rb_tree_node继承了_Rb_tree_node_base,在基础上添加了 一个数据类型,同时对C++11之前和之后值存储在

了解 

C++11之前(__cplusplus < 201103L:值直接存储在节点内部,作为_Val _M_value_field。访问函数_M_valptr()返回这个值的指针,使用std::__addressof(_M_value_field)来获取其地址。

C++11及以后:值存储在__aligned_buffer<_Val> _M_storage内部。这样做很可能是为了确保数据的适当对齐。访问函数_M_valptr()通过调用_M_storage._M_ptr()返回值的指针,这个函数可能处理对齐问题并返回实际值的指针。

__aligned_buffer是一个模板结构,它提供了一种机制来按照类型_Val的对齐要求分配内存。在C++11及更高版本中,对齐是一个重要的概念,因为它影响数据的访问速度和效率。不正确的数据对齐可能导致性能下降或者在某些平台上引起错误。

_M_storage使用__aligned_buffer为类型_Val的对象提供存储空间。这种方式使得即使在栈上分配时,对象也能保证按照其对齐要求被正确地存储。这对于需要特定对齐要求的类型特别有用,比如SIMD类型(如SSE和AVX指令集中使用的类型)或者其他需要特定对齐以优化硬件性能的数据类型。

在C++11之前,没有标准的方法来指定或查询类型的对齐要求,因此_M_storage的使用也体现了对于老版本C++的向后兼容性考虑。在C++11及以后版本中,alignofalignas关键字引入了对齐的标准支持,允许开发者更精确地控制数据的对齐方式。__aligned_buffer<_Val> _M_storage是一种高级技术,用于确保类型_Val的对象在红黑树节点内部以正确的对齐方式存储,这对于保持数据结构的性能和正确性是非常重要的。

rb_tree的迭代器 

template<typename _Tp>struct _Rb_tree_iterator{typedef _Tp  value_type;typedef _Tp& reference;typedef _Tp* pointer;typedef bidirectional_iterator_tag iterator_category; //迭代器类型typedef ptrdiff_t                  difference_type; //两个迭代器间距离typedef _Rb_tree_iterator<_Tp>        _Self;typedef _Rb_tree_node_base::_Base_ptr _Base_ptr;//节点指针typedef _Rb_tree_node<_Tp>*           _Link_type;//节点指针//ctor_Rb_tree_iterator() _GLIBCXX_NOEXCEPT: _M_node() { }explicit_Rb_tree_iterator(_Link_type __x) _GLIBCXX_NOEXCEPT: _M_node(__x) { }referenceoperator*() const _GLIBCXX_NOEXCEPT{ return *static_cast<_Link_type>(_M_node)->_M_valptr(); }//操作符重载返回节点指针pointeroperator->() const _GLIBCXX_NOEXCEPT{ return static_cast<_Link_type> (_M_node)->_M_valptr(); }_Self&operator++() _GLIBCXX_NOEXCEPT{_M_node = _Rb_tree_increment(_M_node);//这个函数的实现在4.9中没有找到 用一下其他版本的 其实现原理基本相似return *this;}_Selfoperator++(int) _GLIBCXX_NOEXCEPT{_Self __tmp = *this;_M_node = _Rb_tree_increment(_M_node);//++操作return __tmp;}_Self&operator--() _GLIBCXX_NOEXCEPT//--也没找到 {_M_node = _Rb_tree_decrement(_M_node);return *this;}_Selfoperator--(int) _GLIBCXX_NOEXCEPT{_Self __tmp = *this;_M_node = _Rb_tree_decrement(_M_node);return __tmp;}booloperator==(const _Self& __x) const _GLIBCXX_NOEXCEPT{ return _M_node == __x._M_node; }booloperator!=(const _Self& __x) const _GLIBCXX_NOEXCEPT{ return _M_node != __x._M_node; }_Base_ptr _M_node;};

 rb_tree迭代器定义了5个STL迭代器必须定义的类型。

然后重载了一些运算符。

重点要看一下__rb_tree_iterator 的 operator++ 跟 operator--。它们分别调用了实际是调用__rb_tree_base_iterator的increment跟decrement。

迭代器前移/后移的时候会按key的顺序找到下一个/上一个结点。

void increment()
{if (node->right != 0) {node = node->right;while (node->left != 0)node = node->left;}else {base_ptr y = node->parent;while (node == y->right) {node = y;y = y->parent;}if (node->right != y)node = y;}
}void decrement()
{if (node->color == __rb_tree_red &&node->parent->parent == node)node = node->right;else if (node->left != 0) {base_ptr y = node->left;while (y->right != 0)y = y->right;node = y;}else {base_ptr y = node->parent;while (node == y->left) {node = y;y = y->parent;}node = y;}
}

rb_tree实现与普通的红黑树类似。

template <class Key, class Value, class KeyOfValue, class Compare,class Alloc = alloc>
class rb_tree {// rb_tree的基本定义
protected:typedef __rb_tree_node_base* base_ptr;typedef __rb_tree_node<Value> rb_tree_node;typedef simple_alloc<rb_tree_node, Alloc> rb_tree_node_allocator;
public:typedef Key key_type;typedef Value value_type;typedef value_type* pointer;typedef value_type& reference;typedef rb_tree_node* link_type;typedef size_t size_type;typedef ptrdiff_t difference_type;typedef __rb_tree_iterator<value_type, reference, pointer> iterator;link_type header;// ...// 主要接口iterator begin() { return leftmost(); } // 返回最左边的结点(最小key)iterator end() { return header; }iterator insert_equal(const value_type& x); // 插入元素 并允许键值相同pair<iterator,bool> insert_unique(const value_type& x); // 插入元素 键值是独一无二的};

insert_equal插入元素,允许元素重复;insert_unique插入元素,不允许元素重复。

 

 

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

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

相关文章

随机分布模型

目录 前言 一、离散型随机变量 1.1 0-1分布 1.2 二项分布 1.3 帕斯卡分布 1.4 几何分布 1.5 超几何分布 1.6 泊松分布 二、连续型随机变量 2.1 均匀分布 2.2 指数分布 2.3 高斯分布/正态分布 2.4 分布&#xff08;抽样分布&#xff09; 2.5 t分布&#xff08;抽样…

matlab经验模式分解的R波检测算法

1、内容简介 略 56-可以交流、咨询、答疑 2、内容说明 略 心血管疾病是威胁人类生命的主要疾病之一&#xff0c;而心电信号&#xff08;electrocardiogram, ECG&#xff09; 则是评价心脏功能的主要依据&#xff0c;因此&#xff0c;关于心电信号检测处理的研究一直为各方所…

ROS的pluginlib学习总结一

在开发中需要使用到插件&#xff0c;因此学习了下pluginlib的一些使用&#xff0c;学习的还不够透彻&#xff0c;先记录一下这几天的学习结果。 关于ROS中pluginlib的使用主要参考的是《ROS的pluginlib的理解与实例》与《ROS专题----pluginlib简明笔记》这两篇文章。第一篇中讲…

react中修改state中的值无效?

// 初始化state state {personArr:[{name:张三,id:1},{name:李四,id:2},{name:王五,id:3}] }componentDidMount(){const newName 赵六const indexUpdate 1const newArr this.state.personArr.map((item,index)>{if(indexUpdate index){return {...item,name:newName}}e…

危险的代码!

//删除 getMatchmakers_delete() {this.isLoadingtrue;// Make an Axios POST request to your backend endpointaxios.delete(config_ipadministrator_id_and_matchmaker_name_delete, {administratorId: this.id,matchmakerName: this.matchmakerName_search}).then(response…

Nest.js权限管理系统开发(六)新建模块

本文相关文档&#xff1a;NestJS 中文网 创建模块 nest g命令 我们知道一个模块往往包含controller、module、service等文件&#xff0c;为了方便我们创建这些文件&#xff0c;nest cli提供了一些命令&#xff1a; 生成模块 (nest g mo) 以保持代码井井有条并建立清晰的边界…

适合新手博主站长使用的免费响应式WordPress博客主题JianYue

这款JianYue主题之所以命名为 JianYue&#xff0c;意思就是简单而不简约的。是根据Blogs主题优化而成&#xff0c;剔除了一些不必要的功能及排版&#xff0c;仅保留一种博客布局&#xff0c;让新手站长能够快速手上WordPress。可以说这款主题比较适合新手博主站长使用&#xff…

SpringCloud-Docker原理解析

Spring Cloud和Docker的结合为微服务架构的部署和管理提供了强大的支持。本文深入剖析Spring Cloud与Docker的集成原理&#xff0c;从服务注册与发现、配置管理、负载均衡到容器化部署等方面展开详细解析。探讨Spring Cloud如何利用Docker容器技术实现服务的弹性伸缩&#xff0…

【深度学习】CIFAR10图像分类

案例3&#xff1a;PyTorch实战: CIFAR10图像分类 1 任务目标 1.1 用多层感知机(MLP)和卷积网络(ConvNet)完成CIFAR10分类 使用PyTorch分别实现多层感知机(MLP)和卷积网络(ConvNet)&#xff0c;并完成CIFAR10数据集&#xff08;http://www.cs.toronto.edu/~kriz/cifar.html&a…

力扣:134. 加油站

1.分为三种情况&#xff0c;一种为总存储的油小于总消耗的油&#xff0c;这样不能跑一圈。二种为从0结点开始就没有断过油。三种为中间有断油过&#xff0c;从后面向前遍历后填过剩下油的最小值&#xff0c;这个点就是出发点。 class Solution {public int canCompleteCircui…

[C++]C++中memcpy和memmove的区别总结

这篇文章主要介绍了C中memcpy和memmove的区别总结,这个问题经常出现在C的面试题目中,需要的朋友可以参考下 变态的命名 我们在写程序时&#xff0c;一般讲究见到变量的命名&#xff0c;就能让别人基本知道该变量的含义。memcpy内存拷贝&#xff0c;没有问题;memmove&#xff…

测试环境搭建整套大数据系统(七:集群搭建kafka(2.13)+flink+hudi+dinky)

一&#xff1a;搭建kafka。 1. 三台机器执行以下命令。 cd /opt wget wget https://dlcdn.apache.org/kafka/3.6.1/kafka_2.13-3.6.1.tgz tar zxvf kafka_2.13-3.6.1.tgz cd kafka_2.13-3.6.1/config vim server.properties修改以下俩内容 1.三台机器分别给予各自的broker_id…

AIGC实战——扩散模型(Diffusion Model)

AIGC实战——扩散模型 0. 前言1. 去噪扩散概率模型1.1 Flowers 数据集1.2 正向扩散过程1.3 重参数化技巧1.4 扩散规划1.5 逆向扩散过程 2. U-Net 去噪模型2.1 U-Net 架构2.2 正弦嵌入2.3 ResidualBlock2.4 DownBlocks 和 UpBlocks 3. 训练扩散模型4. 去噪扩散概率模型的采样5. …

STM32 4位数码管和74HC595

4位数码管 在使用一位数码管的时候&#xff0c;会用到8个IO口&#xff0c;那如果使用4位数码管&#xff0c;难道要使用32个IO口吗&#xff1f;肯定是不行的&#xff0c;太浪费了IO口了。把四个数码管全部接一起共用8个IO口&#xff0c;然后分别给他们一个片选。所以4位数码管共…

✅技术社区项目—JWT身份验证

通用的JWT鉴权方案 JWT鉴权流程 基本流程分三步: ● 用户登录成功之后&#xff0c;后端将生成的jwt返回给前端&#xff0c;然后前端将其保存在本地缓存; ● 之后前端与后端的交互时&#xff0c;都将iwt放在请求头中&#xff0c;比如可以将其放在Http的身份认证的请求头 Author…

C语言编程安全规范

目的 本规范旨在加强编程人员在编程过程中的安全意识,建立编程人员的攻击者思维,养成安全编码的习惯,编写出安全可靠的代码。 2 宏 2.1 用宏定义表达式时,要使用完备的括号 2.2 使用宏时,不允许参数发生变化 3 变量 3.1 所有变量在定义时必须赋初值 变量声明赋予初值,可…

试验铁底板安装流程——北重厂家

试验台铁底板安装流程有哪些&#xff1a; 准备工作&#xff1a;将地面清理干净&#xff0c;并确认地面平整无明显障碍物。 安装定位桩&#xff1a;在地面上标出台铁的位置&#xff0c;并使用锤子和钉子或其他固定工具&#xff0c;将定位桩固定在地面上。 准备底板&#xff1a…

B端系统:导航机制设计,用户体验提升的法宝

Hi&#xff0c;大家好&#xff0c;我是贝格前端工场&#xff0c;从事8年前端开发的老司机。很多B端系统体验不好很大一部分原因在于导航设计的不合理&#xff0c;让用户无所适从&#xff0c;大大降低了操作体验&#xff0c;本文着重分析B端系统的导航体系改如何设计&#xff0c…

$attrs

一、概念 vue官网定义如下: 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过v-bind="$attrs"传入内部组件——在创建…

vscode 离线安装--扩展包

在很多情况下&#xff0c;我们不能联网下载 vscode 的扩展包&#xff0c;下面记录一下怎么离线安装和下载 扩展包的下载地址 下载地址 安装指令 code --install-extension 文件名wsl vscode 如果是 win wsl 的环境&#xff0c;除了在 win 的 vscode 下要安装一次扩展包&…