C++ 红黑树封装map和set

目录

前言

 1.红黑树的改造

1.1主题框架

1.2迭代器

operator++ ()

begin()和end()

 1.3红黑树相关接口的改造

Find函数的改造

Insert 函数的改造 

2.红黑树改造的完整代码

3.Set的封装

4.Map的封装


前言

map 和 set 的底层本质上还是复用,通过对红黑树的改造,再分别套上一层 map 和 set 的 “壳子”,以达到 “一树二用” 的目的

在改造红黑树的时候,我们将会面对几个问题。

  1. 对红黑树的改造,关联式容器中存储的是<key, value>的键值对,K为key的类型,如果是set的话,则是K,如果是map,则为pair<k,v>。如何用一个节点结构控制两种类型,使用类模板参数T
  2. 在插入的过程中,因为是泛型编程,需要用红黑树给map和set套一个“壳子”,在比较大小的过程中,pair的比较是通过first和second共同决定的,因此插入数据时不能直接比较,要在 set 和 map 类中实现一个 KeyOfT 的仿函数,以便单独获取两个类型中的 key数据
  3. 关于 key 的修改问题。在STL库中,set 和 map 的 key 都是不能修改的,因为要符合二叉搜索树的特性,但是 map 中的 value 又是可以修改的。这个问题需要单独处理。
  4. 红黑树相关接口的改造。其中包括对 Find 和 Insert 函数的改造,特别是 Insert,因为在 map 里实现 operator[] 时需要依赖 Insert 函数。

 1.红黑树的改造

1.1主题框架

(1) K 是给find,erase用的,T 是给节点,insert用的

(2) KeyOfT 是由于下面需要比较,但是比较时不知道T的类型, set是key类型的比较,map是pair类型的比较,要统一变成key的比较

红黑树的结点构造

using namespace std;
enum color
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>* left;RBTreeNode<T>* right;RBTreeNode<T>* _parent;T data;color col;RBTreeNode(const T& data):left(nullptr), right(nullptr), _parent(nullptr), data(data), col(RED){}
};

主体框架

class RBTree
{typedef RBTreeNode<T> Node;
public:typedef __TreeIterator<T, T&, T*> iterator; //迭代器typedef __TreeIterator<T, const T&, const T*> const_iterator; //const迭代器.......//相应的接口
private:	Node* root = nullptr;
};

1.2迭代器

template<class T, class Ref, class Ptr>
struct __TreeIterator
{typedef RBTreeNode<T> Node;typedef __TreeIterator<T, Ref, Ptr> Self;Node* _node;__TreeIterator(Node* node):_node(node){}Ref operator*(){return _node->data;}Ptr operator->(){return &_node->data;}Self& operator++(){if (_node->right){// 下一个就是右子树的最左节点Node* cur = _node->right;while (cur->left){cur = cur->left;}_node = cur;}else{// 左子树 根 右子树// 右为空,找孩子是父亲左的那个祖先Node* cur = _node;Node* parent = cur->_parent;while (parent && cur == parent->right){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;}
};

operator++ ()

这里需要重点提的是operator++

它与以往的容器不相同,这是通过二叉树构建,根据它的中序遍历实现++ 

为了完成二叉树的中序遍历,我们需要从局部的某一过程考虑,就是假设 it 已经走到了某一节点,要找到下一个访问的节点,分为两种情况:

1.当前节点的右子树不为空

当结点是it的时候,说明左子树已经全部访问完了,开始访问右子树,右子树不为空,我们下一个访问的节点是15,所以要找到右子树的最左节点。

 2. 当前节点的右子树为空:

当it为该节点时,右子树为空,访问的下一个节点时8,因为it被访问完时,代表着根节点已经访问完了,要找祖先作为祖先父亲的左节点即可。

begin()和end()

iterator begin()
{Node* cur = root;while (cur&&cur->left){cur = cur->left;}return iterator(cur);
}
iterator end()
{return iterator(nullptr);
}
const_iterator begin()const
{Node* cur = root;while (cur&&cur->left){cur = cur->left;}return const_iterator(cur);
}
const_iterator end()const
{	return const_iterator(nullptr);
}

 1.3红黑树相关接口的改造

Find函数的改造

查找成功,返回查找到的那个节点的迭代器,查找失败,就返回 nullptr。

Iterator Find(const K& key)
{Node* cur = _root;while (cur){if (cur->_data > key)cur = cur->_left;else if (cur->_data < key)cur = cur->_right;elsereturn Iterator(cur);}return End();
}

Insert 函数的改造 

map 里的 operator[] 需要依赖 Insert 的返回值

pair<Iterator, bool> Insert(const T& data)
{if (_root == nullptr){_root = new Node(data);return make_pair(Iterator(_root), true);}KeyOfT kot;  仿函数对象Node* cur = _root;Node* parent = nullptr;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;}elsereturn make_pair(Iterator(cur), false);}cur = new Node(data);Node* newnode = cur;//此处省略变色+旋转部分的代码……

剩下的变色或旋转代码上一篇讲到过

红黑树

2.红黑树改造的完整代码

#include <iostream>
#include <assert.h>
using namespace std;//枚举颜色
enum Colour
{RED,BLACK
};//节点类
template <class T>
struct RBTreeNode
{T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;//pair<K, V> _kv;Colour _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr), _parent(nullptr), _data(data), _col(BLACK){}
};//迭代器类
template <class T, class Ref, class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ref, Ptr> Self;Node* _node;RBTreeIterator(Node* node):_node(node){}bool operator!=(const Self& s){return s._node != _node;}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}//从局部的某一个过程考虑Self& operator++(){if (_node->_right){//右不为空,右子树的最左节点就是下一个访问的节点Node* leftMost = _node->_right;while (leftMost->_left)leftMost = leftMost->_left;_node = leftMost;}else{//右为空,代表当前节点所在的子树已经访问完了,下一个访问的节点是祖先//沿着到根节点的那个路径查找,孩子是父亲左的那个祖先节点就是下一个访问的节点Node* cur = _node;Node* parent = cur->_parent;//循环找祖先while (parent && cur == parent->_right){cur = parent;parent = parent->_parent;}_node = parent;}return *this;}
};//K 是给find,erase用的,T 是给节点,插入用的
// KeyOfT 是由于下面需要比较,但是比较时不知道T的类型,
// set是key类型的比较,map是pair类型的比较,要统一变成key的比较template <class K, class T, class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef RBTreeIterator<T, T&, T*> Iterator;typedef RBTreeIterator<T, const T&, const T*> ConstIterator;//中序遍历,找树的最左节点Iterator Begin(){//Node* cur = _root;Node* leftMost = _root;while (leftMost->_left)leftMost = leftMost->_left;return Iterator(leftMost);}Iterator End(){return Iterator(nullptr);}ConstIterator Begin()const{//Node* cur = _root;Node* leftMost = _root;while (leftMost->_left)leftMost = leftMost->_left;return ConstIterator(leftMost);}ConstIterator End()const{return ConstIterator(nullptr);}Iterator Find(const K& key){Node* cur = _root;while (cur){if (cur->_data > key)cur = cur->_left;else if (cur->_data < key)cur = cur->_right;elsereturn Iterator(cur);}return End();}pair<Iterator, bool> Insert(const T& data){if (_root == nullptr){_root = new Node(data);return make_pair(Iterator(_root), true);}KeyOfT kot;Node* cur = _root;Node* parent = nullptr;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;}elsereturn make_pair(Iterator(cur), false);}cur = new Node(data);Node* newnode = cur;//新增节点,颜色为红色cur->_col = RED;if (kot(parent->data) < kot(data)){parent->right = cur;cur->_parent = parent;}else{parent->left = cur;cur->_parent = parent;}while (parent && parent->col == RED){Node* grandparent = parent->_parent;if (parent == grandparent->left){Node* uncle = grandparent->right;if (uncle && uncle->col == RED){parent->col = uncle->col = BLACK;if (grandparent == root){grandparent->col = BLACK;return make_pair(root, true);}else{grandparent->col = RED;cur = grandparent;parent = cur->_parent;}}else{if (cur = parent->left){RotateR(grandparent);parent->col = RED;grandparent->col = BLACK;}else{RotateL(parent);RotateR(grandparent);cur->col = BLACK;grandparent->col = RED;}break;}}else//parent=grandparent->right{Node* uncle = grandparent->left;if (uncle && uncle->col == RED){parent->col = uncle->col = BLACK;if (grandparent == root){grandparent->col = BLACK;return make_pair(root, true);}else{grandparent->col = RED;cur = grandparent;parent = cur->_parent;}}else{if (cur == parent->right){RotateL(grandparent);parent->col = BLACK;grandparent->col = RED;}else{RotateR(parent);RotateL(grandparent);cur->col = BLACK;grandparent->col = RED;}break;}}}}root->col = BLACK;return make_pair(newnode, true);
}private:void RotateL(Node* parent)
{Node* subR = parent->right;Node* subRL = subR->left;subR->left = parent;parent->right = subRL;if (subRL){subRL->_parent = parent;}Node* parentParent = parent->_parent;parent->_parent = subR;if (parent == root){root = subR;subR->_parent = nullptr;}else{if (parentParent->left == parent){parentParent->left = subR;}else{parentParent->right = subR;}subR->_parent = parentParent;}}
void RotateR(Node* parent)
{Node* subL = parent->left;Node* subLR = parent->right;parent->left = subLR;subL->right = parent;if (subLR){subLR->_parent = parent;}Node* parentParent = parent->_parent;parent->_parent = subL;if (root == parent){root = subL;subL->_parent = nullptr;}else{if (parentParent->left == parent){parentParent->left = subL;}else{parentParent->right = subL;}subL->_parent = parentParent;}
}    Node* _root = nullptr;};

3.Set的封装

set的底层为红黑树,因此只需在set内部封装一棵红黑树,即可将该容器实现出来。

为了解决 set 中 key 值不能修改的问题,在传给 RBTree 的第二个模板参数前加 const 即可

namespace bit
{template<class K>class set{public:struct SetKeyOfT{const K& operator()(const K& key){return key;}};typedef typename RBTree<K, K, SetKeyOfT>::const_iterator iterator;typedef typename RBTree<K, K, SetKeyOfT>::const_iterator const_iterator;iterator begin() const{return _t.begin();}iterator end() const{return _t.end();}pair<iterator, bool> insert(const K& key){return _t.Insert(key);}private:RBTree<K, K, SetKeyOfT> _t;};
}
void Print(const cc::set<int>& s)
{auto it = s.begin();while (it != s.end()){cout << *it << " ";++it;}cout << endl;
}//测试代码
void Test_set()
{//int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };int a[] = { 16,3,7,11,9,26,18,14,15 };cc::set<int> s;for (auto e : a)s.insert(e);for (auto e : s)cout << e << " ";cout << endl;Print(s);
}

4.Map的封装

#include "RBTree.h"namespace cc
{template<class K, class V>class map{//获取 pair 中的 keystruct MapOfT{const K& operator()(const pair<K, V>& kv){return kv.first;}};public:typedef typename RBTree<K, pair<const K, V>, MapOfT>::Iterator iterator;typedef typename RBTree<K, pair<const K, V>, MapOfT>::ConstIterator const_iterator;iterator begin(){return _t.Begin();}iterator end(){return _t.End();}const_iterator begin()const{return _t.Begin();}const_iterator end()const{return _t.End();}pair<iterator, bool> insert(const pair<K, V>& kv){return _t.Insert(kv);}iterator find(const K& key){return _t.Find(key);}//给一个key,返回value的引用V& operator[](const K& key){pair<iterator, bool> ret = insert(make_pair(key, V()));return ret.first->second;}private:RBTree<K, pair<const K,V>, MapOfT> _t;};
}//测试代码
void Test_map()
{cc::map<string, string> dict;dict.insert({ "left","左边" });dict.insert({ "right","右边" });dict.insert({ "insert","插入" });dict["left"] = "剩余,左边";cc::map<string, string>::iterator it = dict.begin();while (it != dict.end()){//it->first += 'x'; //errit->second += 'y'; //okcout << it->first << ":" << it->second << endl;++it;}cout << endl;
}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

freeRDP OPenssl

libusb需要下载 我使用的是VS2019编译 所以需要include 与vs2019 在cmake里面修改路径 C:/Users/JPM/source/repos/freeRDP/FreeRDP-stable-2.0/libusb-1.0.24/include/libusb-1.0 C:/Users/JPM/source/repos/freeRDP/FreeRDP-stable-2.0/libusb-1.0.24/VS2019/MS64/static/l…

pycharm24.2运行框中无法输入中文但是可以粘贴中文、输入英文、数字

文章目录 一、问题描述二、解决办法解决办法1解决办法2解决办法3 一、问题描述 pycharm24.2版本中运行框中无法输入中文&#xff0c;敲击空格键发现中文并没有输入进去: 但是可以粘贴中文: 输入英文、数字没有问题。 二、解决办法 该问题为pycharm24.2版本问题。 解决办法…

AI无人直播新标杆,一站式直播解决方案:打造专属舞台!

AI无人直播新标杆&#xff0c;一站式直播解决方案&#xff1a;打造专属舞台&#xff01; 在数字化浪潮的汹涌澎湃中&#xff0c;AI技术正以前所未有的速度渗透至各行各业&#xff0c;其中&#xff0c;直播行业作为数字内容传播的重要阵地&#xff0c;正经历着一场由AI引领的深刻…

OpenHarmony(鸿蒙南向)——平台驱动指南【MIPI CSI】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 持续更新中…… 概述 功能简介 CSI&#xff08;Camera Serial Interface&#xf…

数字人形象自定义制作:readyplayer

网址&#xff1a; https://readyplayer.me/ 支持上传照片和拍照&#xff0c;会自动识别变成卡通风格 其他选项是配置选项&#xff1a;穿着、样貌等 上面弄好后右上角点击next&#xff0c;创建的模型可以下载3d glb文件 glb文件在线打开&#xff1a; https://gltf-viewer.d…

SpinalHDL之语义(Semantic)(一)

本文作为SpinalHDL学习笔记第六十九篇,介绍SpinalHDL的赋值(Assignments)。 目录: 1.赋值(Assignments) 2.位宽检查(Width checking) 3.组合逻辑环路(Combinatorial loops) ⼀、赋值(Assignments) SpinalHDL中有多个赋值运算: //因为硬件的并发性, `a`的值⼀直是1 val a…

CrossOver24支持的游戏有那些

CrossOver刚刚更新了24版本&#xff0c;支持《地平线零之曙光》、《以撒的结合&#xff1a;重生》等游戏。一起来看看它有哪些更新吧&#xff01; 一、功能优化 - 更新 Wine 至最新的稳定版 Wine 9.0&#xff0c;引入了 7000多个更新和针对各种软件游戏的优化。 - 更新 Wine M…

Azure Kinect 人体跟踪关节

Azure Kinect 人体跟踪关节 azure kinect dk 提取人体骨骼 要在Azure Kinect DK上提取人体骨骼&#xff0c;你需要使用Azure Kinect SDK和OpenPose库。以下是一个简化的代码示例&#xff0c;展示如何集成这两个库来提取骨骼关键点&#xff1a; 首先&#xff0c;确保你已经安装…

【YashanDB知识库】decode函数中的子查询被不必要地多次执行

本文内容转自YashanDB官网&#xff0c;具体内容请见https://www.yashandb.com/newsinfo/7441387.html?templateId1718516 问题现象 客户向yashandb下发的SQL语句执行时间超过6分钟仍未出结果 问题的风险及影响 SQL语句性能慢&#xff0c;影响客户业务 问题影响的版本 所…

蜂窝物联网全网通sim卡切网技术方案软硬件实现教程(设备根据基站信号质量自动切网)

01 物联网系统中为什么要使用三合一卡 三合一卡为用户解决了单一运营商网络无法全覆盖的缺陷&#xff0c;避免再次采购的经济成本以及时间成本和因没有信号设备停止工作造成的损失&#xff0c;保证仅需一次采购并提高设备工作效率和入网活跃度。例如下面地区的设备&#xff0…

RPA自动化流程机器人有哪些优势?

在数字化快速推进的大背景下&#xff0c;人工智能正在以前所未有的速度改变着生活和生产方式&#xff0c;而RPA自动化流程机器人作为其中一种最重要的革命性的技术&#xff0c;已经成为企业数字化中不可或缺的重要力量&#xff0c;让员工加速从“重复性工作”中摆脱出来。 金智…

【芋道源码】gitee很火的开源项目pig——后台管理快速开发框架使用笔记(微服务版之本地开发环境篇)

后台管理快速开发框架使用笔记&#xff08;微服务版之本地开发环境篇&#xff09; 后台管理快速开发框架使用笔记&#xff08;微服务版之本地开发环境篇&#xff09; 后台管理快速开发框架使用笔记&#xff08;微服务版之本地开发环境篇&#xff09;前言一、如何获取项目&#…

推荐十款主流的采购管理系统,为企业选型提供参考!

大家都明白采购对制造型企业的重要性&#xff0c;但是在面对市场上琳琅满目的采购管理系统企业却不知道该如何选择&#xff0c;不要担心。 本篇文章将对市面上知名的采购管理系统进行综合测评&#xff0c;深入剖析这些平台的特点与优势。看完这篇内容&#xff0c;你将对不同采…

前台项目启动/打包报错 Error: error:0308010C:digital envelope routines::unsupported

在package.json中修改启动/打包语句 如图&#xff0c;我这里是打包时候报错&#xff0c;就在build里前面加上 set NODE_OPTIONS--openssl-legacy-provider && 再次打包&#xff0c;成功。

【OpenAI o1思维链CoT必看论文】谷歌“思维链提示“让AI更懂人类推理

原创 超 超的闲思世界 AI的推理能力正迎来一场重大突破。谷歌大脑团队最新开发的"思维链提示"方法&#xff0c;让大型语言模型在复杂推理任务上展现出惊人的进步。这项创新技术无需对模型进行额外训练&#xff0c;却能显著提升AI的推理能力&#xff0c;让机器的思…

微电网与大电网主动同步控制

前言 大电网正常运行时&#xff0c;微电网通过大电网得到正常的电压频率参数支撑&#xff0c;大电网故障时&#xff0c;微电网的电压和频率支撑需要通过分布式电源提供&#xff0c;从而保持自身独立运行。分布式电源提供的电压信息会因为自身的下垂特性随本地负荷的改变不断变…

vue 中获取数值但是只获取到了 Promise 属性,获取不到其中的值

左边的请求能获取到数据&#xff0c;右边的不行&#xff1f; 改成这样即可

【雅特力AT32】IIC使用指南_附读写EEPROM案例

目录 1.12C接口简介 2.12C接口通信 2.1主机通信流程 2.1.1 主机通信初始化 1>主机时钟初始化 2>主机通信初始化 3>主机 10 bits 寻址的特殊时序初始化 2.1.2 主机通信初始化软件接口 2.1.3 主机发送流程 2.1.4 主机发送流程软件接口 2.1.5 主机接收流程 2.1.6 主机接收…

leetcode 1361. 验证二叉树

二叉树上有 n 个节点&#xff0c;按从 0 到 n - 1 编号&#xff0c;其中节点 i 的两个子节点分别是 leftChild[i] 和 rightChild[i]。 只有 所有 节点能够形成且 只 形成 一颗 有效的二叉树时&#xff0c;返回 true&#xff1b;否则返回 false。 如果节点 i 没有左子节点&#…

【Ubuntu】Ubuntu安装编译C/C++环境简易版教程

环境 操作系统&#xff1a;ubuntu-22.04.4-desktop-amd64.iso 安装 第一步:更新软件包列表&#xff0c;检查可用的软件包更新 sudo apt update在这一步&#xff0c;我们可以确保系统中的软件包列表是最新的&#xff0c;以便后续的软件包管理操作。 第二步&#xff1a;安装…