map和set的设计以及红黑树的设计

1.map和set的底层是红黑树

2.map和set在STL是容器,在我看来,不过也是封装了平衡二叉搜索树红黑树的适配器

我们先看红黑树的设计,看完后map和set的封装易如反掌

#pragma once
#include<utility>
#include<iostream>
using namespace std;
enum Colour
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colour _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(RED){}
};template<class T, class Ref, class Ptr>//T 确定迭代器指向什么类型节点,reference 引用,ptr  pointer指针
struct __RBTreeIterator //不是struct就不能直接访问迭代器成员_node
{typedef RBTreeNode<T> Node;typedef __RBTreeIterator<T, Ref, Ptr> Self;Node* _node;__RBTreeIterator(Node* node):_node(node){}__RBTreeIterator(const __RBTreeIterator<T, T&, T*>& it)//set中begin()是红黑树begin()的封装,红黑树是普通红黑树成员,红黑树begin()返回的是普通迭代器,但是set中用Iterator来接收返回值,是红黑树中const_iterator的重命名,所以要加一个函数,用iterator构造出const——iterator,才能正常返回: _node(it._node){}Ref operator*(){return _node->_data;}Ptr operator->()//调用时少一个->,编译器会出手{return &_node->_data;}bool operator!=(Self it){return _node != it._node;}Self& operator++()//中序左根右    迭代器不销毁传引用{if (_node->_right){Node* tmp = _node->_right;while (tmp->_left){tmp = tmp->_left;}_node = tmp;}else{Node* cur = _node,*parent=_node->_parent;while (parent && parent->_right == cur)//cur在右边cur遍历根早遍历过了  如果parent为空说明是最右端节点,++置空{cur = cur->_parent;parent = parent->_parent;}_node = parent;}return *this;}Self& operator--()//--就是++反过来的顺序,右根左{if (_node->_left){Node* tmp = _node->_left;while (tmp->_right){tmp = tmp->right;}_node = tmp;return *this;}else{Node* cur = _node, * parent = _node->_parent;while (parent && parent->_left == cur)//右根左 cur在左边cur遍历根早遍历过了  parent为空则是最左端节点{cur = cur->_parent;parent = parent->_parent;}_node = parent;return *this;}}};template<class K,class T,class KeyOfT>//KeyOfT是仿函数,用来控制节点的比较,因为node里是T,没有K
class RBTree
{typedef RBTreeNode<T>  Node;
public:~RBTree(){_Destroy(_root);_root = nullptr;}typedef __RBTreeIterator<T, T&, T*> iterator;typedef __RBTreeIterator<T, const T&, const T*> const_iterator;iterator begin()//传普通红黑树的this指针{Node* tmp = _root;while (tmp && tmp->_left){tmp = tmp->_left;}return tmp;//tmp构造一个const临时iterator,再赋值}iterator end(){return nullptr;}const_iterator begin()const//传const红黑树的this指针,返回const红黑树迭代器{Node* tmp = _root;while (tmp && tmp->_left){tmp = tmp->_left;}return tmp;}const_iterator end()const{return nullptr;}Node* Find(const K& key){Node* cur = _root;KeyOfT kot;while (cur){if (kot(cur->_data) > key){cur = cur->_left;}else if (kot(cur->_data) < key){cur = cur->_right;}else{return cur;}}return nullptr;}pair<iterator, bool> Insert(const T& data){if (_root == nullptr){_root=new Node(data);//BUG--没有把节点变黑_root->_col = BLACK;return make_pair(_root, true);}Node* cur = _root,*parent=nullptr;KeyOfT kot;while (cur){if (kot(cur->_data) > kot(data)){parent = cur;cur = cur->_left;}else if (kot(cur->_data) < kot(data)){parent = cur;cur = cur->_right;}else{return make_pair(iterator(cur), false);}}cur = new Node(data);if (kot(parent->_data) > kot(data)){parent->_left = cur;}else{parent->_right = cur;}Node* newnode = cur;cur->_parent = parent;while (parent && parent->_col==RED)//插入的是红色,如果parent为黑就不用调整,为红就调整{//因为parent是红色,所以parent不是根,一定有爷爷节点且为黑色Node* grandfather = parent->_parent;if (grandfather->_left == parent){Node* uncle = grandfather->_right;if (uncle && uncle->_col == RED){parent->_col = uncle->_col=BLACK;grandfather->_col = RED;//爷爷节点的父节点可能是红,还得向上调整cur = grandfather;parent = cur->_parent;}else//叔叔不存在或者是黑{if (cur == parent->_left)//cur的位置不同旋转就不同{rotateR(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{rotateL(parent);rotateR(grandfather);grandfather->_col = RED;cur->_col = BLACK;}break;//新子树根节点是黑色,无需再向上调}}else//爷爷的右边是parent{Node* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//叔叔为红时调整不需要考虑cur的位置{uncle->_col =parent->_col= BLACK;grandfather->_col = RED;cur = grandfather;parent = cur->_parent;}else{if (cur == parent->_right){rotateL(grandfather);grandfather->_col = RED;parent->_col = BLACK;}else{rotateR(parent);rotateL(grandfather);grandfather->_col = RED;cur->_col = BLACK;}break; // 新子树根节点是黑色,无需再向上调}}}//调整完可能有根节点为红_root->_col = BLACK;return make_pair(newnode, true);}bool isRBTree(){if (_root == nullptr) return true;if (_root->_col == RED){cout << "根节点是红" << endl;return false;}int benchmark = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)benchmark++;cur = cur->_left;}return _Check(_root, 0, benchmark);}
private:bool _Check(Node* root, int blacknum, int benchmark){if (root == nullptr)//root为空说明一条路径结束{if (benchmark == blacknum){return true;}else{cout << "路经黑色节点数量不一" << endl;return false;}}if (root->_col == BLACK) blacknum++;if (root->_col == RED && root->_parent && root->_parent->_col == RED){cout << "有连续红色节点" << endl;return false;}return _Check(root->_left, blacknum, benchmark) &&_Check(root->_right, blacknum, benchmark);}void _Destroy(Node* root){if (root == nullptr)return;_Destroy(root->_left);_Destroy(root->_right);delete root;}void rotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;Node* ppnode = parent->_parent;subR->_left = parent;parent->_parent = subR;if (ppnode == nullptr){_root = subR;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subR;}else{ppnode->_right = subR;}subR->_parent = ppnode;}}void rotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* ppnode = parent->_parent;subL->_right = parent;parent->_parent = subL;if (parent == _root){_root = subL;_root->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}Node* _root = nullptr;
};

1.红黑树结点的设计

三个指针一个T类型数据,还有一个颜色

2.红黑树的模板参数

第一个是节点排序所用的类型K,第二个是节点里数据的类型T,第三个是把T变成K的仿函数类型

3.g++的红黑树设计思路是一个哨兵节点,一个节点数量

g++中哨兵节点(_M_header)的结构

  • _M_header->_M_parent:指向红黑树的 根节点(如果树非空)。

  • _M_header->_M_left:指向树的 最左节点(即中序遍历的第一个节点)。

  • _M_header->_M_right:指向树的 最右节点(即中序遍历的最后一个节点)。

template <typename _Key, typename _Val, typename _KeyOfValue, typename _Compare, typename _Alloc>
class _Rb_tree {
private:_Rb_tree_node<_Val>* _M_header;  // 哨兵节点(根节点的父节点)size_t _M_node_count;            // 节点数量_Compare _M_key_compare;         // 比较函数(默认 std::less<_Key>)public:// 插入、删除、查找等操作...
};

但是我上面代码的实现并没有设计哨兵节点,也没有节点个数,就一个根节点

4.红黑树的迭代器设计

红黑树的迭代器和list一样是封装了结点指针的类,第一个模板参数是为了形成结点指针类型,二、三模板参数则是为了实例化出迭代器和const迭代器类

5.g++设计迭代器 和  我上面代码设计的迭代器

g++迭代器的 begin() 和 end() 逻辑

  • begin() 直接返回 _M_header->_M_left(最左节点)。

  • end() 返回 哨兵节点_M_header

自己设计的迭代器的 begin() 和 end() 逻辑:

  • begin() 遍历查找返回最左节点。

  • end() 返回 nullptr。

map和set适配红黑树

#pragma once
#include"RBTree.h"
namespace aqc
{template<class K,class V>class map{public:struct MapKeyOfT{const K& operator()(const pair<const K, V>& kv){return kv.first;}};typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;//如果不加typename可能编译器会识别为类中的静态变量iterator begin(){return t.begin();}iterator end(){return t.end();}pair<iterator,bool> Insert(const pair<const K, V>& kv){return t.Insert(kv);}V& operator[](const K& key){pair<iterator, bool> ret = t.Insert(make_pair(key, V()));return ret.first->second;//iterator->返回的是_data的地址}private:RBTree<K, pair<const K,V>, MapKeyOfT> t;};
}#pragma once
#include"RBTree.h"
namespace aqc
{template<class K>class set{public:struct SetKeyOfT{const K& operator()(const K& key)//如果写成struct外面调不到运算符重载{return key;}};typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;//set中二叉树节点的T即为key,不能被修改typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;iterator begin(){return t.begin();//普通红黑树成员调begin返普通迭代器,接受类型为:const_iterator,因此先前在红黑树加了个const迭代器构造函数}iterator end(){return t.end();}const_iterator begin()const{return t.begin();//const set调用begin:首先普通红黑树调用begin返回普通迭代器,然后接受类型构造}const_iterator end()const{return t.end();}pair<iterator,bool> Insert(const K& key){return t.Insert(key);}private:RBTree<K,K, SetKeyOfT> t;};
}

1.封装map,节点来排序的类型是K,节点里数据的类型pair<const K,V>,还有把T变成K的仿函数类

2.封装set,节点用来排序的类型是K,节点里数据的类型还是K,还有把T变成K的仿函数类

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

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

相关文章

Linux运维——Vim技巧二

Vim技巧 一、管理多个文件1.1、用缓冲区列表管理打开的文件1.2、用参数列表将缓冲区分组1.3、将工作区切分成窗口1.4、用标签页将窗口分组1.5、用:edit命令打开文件1.6、使用:find打开文件1.7、把文件保存到不存在的目录中 二、动作命令在文档中移动2.1、区分实际行与屏幕行2.2…

2025 年 408 真题及答案

2025 年 408 真题 历年408真题及答案下载直通车 1、以下 C 代码的时间复杂度是多少&#xff1f;&#xff08;&#xff09; int count 0; for (int i0; i*i<n; i)for (int j0; j<i; j)count;A O(log2n)B O(n)C O(nlogn)D O(n2) 2、对于括号匹配问题&#xff0c;符号栈…

【MuJoCo仿真】开源SO100机械臂导入到仿真环境

主要参考&#xff1a;https://github.com/jpata/gym-so100/tree/integration/gym_so100/assets/trs_so_arm100 参考&#xff1a;&#xff08;八&#xff09;lerobot开源项目扩展so100的仿真操控&#xff08;操作记录&#xff09;_so100机械臂 仿真-CSDN博客 下载&#xff1a;…

Socat 用法详解:网络安全中的瑞士军刀

Socat 用法详解&#xff1a;网络安全中的强大工具 引言 socat&#xff08;SOcket CAT&#xff09;是一款功能强大的命令行工具&#xff0c;被誉为“网络瑞士军刀”&#xff0c;广泛应用于数据传输、端口转发和网络调试等场景。它支持多种协议和数据通道&#xff08;如文件、管…

永磁同步电机控制算法--基于PI和前馈的位置伺服控制

一、原理介绍 永磁同步伺服系统是包含了电流环、速度环和位置环的三环控制系统。 伺服系统通过电流检测电路和光电编码器检测电动机三相绕组电流和转子位置θ&#xff0c;通过坐标变换&#xff0c;计算出转矩电流分量iq和励磁电流分量id。 位置信号指令与实际转子位置信号的差…

Lucene多种数据类型使用说明

Lucene 作为一款高性能的全文检索引擎库&#xff0c;其核心功能围绕索引和搜索文本数据&#xff0c;但它也支持多种数据类型以满足复杂的应用场景。以下是 Lucene 支持的主要数据类型及其用途的详细说明&#xff1a; 1. 文本类型&#xff08;Text&#xff09; 用途&#xff1a;…

Web网页布局

目录 一、传统的DIVCSS布局&#xff08;使用率最高的&#xff09; 1.div传统的一块块转 2.以猫眼电影为例‘ 3.div布局格式&#xff08;唯一的id属性&#xff0c;不唯一写class重复的&#xff09; 3.2总体布局样式 二、HTML5语义标签CSS3布局 1.把div改为绿色的语义标签…

大模型基础(五):transformers库(下):快速分词器、自动配置类、快速微调

transformers库&#xff08;下&#xff09; 1 快速分词器1.1 Fast 分词器的核心特点1.2 对比示例1.3 何时使用 Fast 分词器&#xff1f;1.4 注意事项 2 自动配置类 AutoConfig2.1 核心功能2.2 基本用法2.3 主要应用场景2.4 常用函数2.5 与具体配置类的区别2.6 注意事项 3 快速微…

在pycharm profession 2020.3上离线安装.whl类型的包(以PySimpleGUI为例)

今天写个小代码&#xff0c;用到了PySimpleGUI。 在pycharm profession 2020.3的项目中的Terminal里运行如下代码即可安装。 python3 -m pip install --force-reinstall --extra-index-url https://PySimpleGUI.net/install PySimpleGUI 安装方法如图&#xff1a; 安装后使用…

SpringBoot整合RabbitMQ(Java注解方式配置)

1.生产端 1. 创建生产者SpringBoot工程 2. 引入start&#xff0c;依赖坐标 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId> </dependency> 3. 编写yml配置&#xff0c;基本…

分析strtol(),strtoul()和strtod()三个函数的功能

字符串转换为数值部分和子字符串首地址的函数有strtol(),strtoul()和strtod()三个函数。 1、strtol()函数 long int strtol(const char *str, char **endptr, int base) //当base0时,若字符串不是以"0","0x"和"0X"开头,则将数字部分按照10进制…

Spring 的事务隔离

在Spring框架中&#xff0c;事务管理是一个非常重要的方面&#xff0c;它允许开发者以声明式的方式定义事务边界&#xff0c;并且通过配置不同的隔离级别来控制并发事务的行为。Spring支持多种事务管理方式&#xff0c;包括编程式事务管理和声明式事务管理&#xff08;如使用Tr…

单片机自动排列上料控制程序

/****L2233 CODE11549 2025 4 18 08:53*******/ /***轴承上料机控制 提升 摇摆 光电检测***/ /***2025 3 21 电机控制PCB板 PAST ***/ /*2.3 2.2 1.2 1.3 1.4 1.5 1.6 1.7 5.3 3.2 ***/ /*启动 解锁 光电 接近 前停 后停 电机前 电机后*/ #include &quo…

力扣119题解

记录 2025.5.5 题目&#xff1a; 思路&#xff1a; 代码: class Solution {public List<Integer> getRow(int rowIndex) {List<Integer> row new ArrayList<Integer>();row.add(1);for (int i 1; i < rowIndex; i) {row.add((int) ((long) row.get(i…

国产化海光C86架构服务器安装windows实录

最近几年与红蓝关系急转直下&#xff0c;尤其是科技领域尤为突出。随之而来的就是软硬件的国产化大潮。由于行业的原因根据要求必须使用国产化服务器、国产化操作系统、国产化数据库、国产化中间件。虽然闭关锁国断开红蓝联系可以在一定程度激发国产化发展&#xff0c;但是不得…

Oracle OCP证书有效期是三年?

这一段时间&#xff0c;网上经常传出消息Oracle OCM认证证书有效期为三年&#xff0c;其实这个假消息&#xff0c;通过博睿谷与Oracle官方人员确认&#xff0c;OCP认证证书有效期是永久的。 OCP证书本身永久有效&#xff0c;但老版本的OCP证书代表着更多的项目经验&#xff0c…

Python 闭包:函数式编程中的魔法变量容器

闭包与匿名函数的常见混淆 在编程社区中&#xff0c;闭包(closure)和匿名函数(anonymous function)经常被混为一谈&#xff0c;这种混淆有其历史根源&#xff1a; 历史发展因素&#xff1a;在早期编程实践中&#xff0c;在函数内部定义函数并不常见&#xff0c;直到匿名函数广…

迅睿CMS导入别站数据库

<?php if (isset($_GET[go])) {$host localhost;// 数据库服务器$username uname;// 数据库用户名$password pwd;// 数据库密码$database database;// 数据库名$cmscid $_GET[cmscid];$mtabcid $_GET[mtabcid];if ($_GET[go] step1) {//第一步&#xff1a;先获取CMS…

基于C++、JsonCpp、Muduo库实现的分布式RPC通信框架

⭐️个人主页&#xff1a;小羊 ⭐️所属专栏&#xff1a;项目 很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~ 目录 项目介绍JsonCpp库简单介绍Muduo库简单介绍C11异步操作——std::future1. 使用 std::async 关联异步任务2. std::packaged_task 配…

EPSG:3857 和 EPSG:4326 的区别

EPSG:3857 和 EPSG:4326 是两种常用的空间参考系统&#xff0c;主要区别在于坐标表示方式和应用场景。以下是它们的核心差异&#xff1a; 1. 坐标系类型 EPSG:4326&#xff08;WGS84&#xff09; 地理坐标系&#xff08;Geographic Coordinate System&#xff09;&#xff0c;基…