用红黑树封装实现map与set

红黑树

红黑树 ,是一种 二叉搜索树 ,但 在每个结点上增加一个存储位表示结点的颜色,可以是 Red
Black 。 通过对 任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路
径会比其他路径长出俩倍 ,因而是 接近平衡
对比AVL树的严格平衡(左右子树高度差不超过1),需要更多的旋转才能控制这个高度
红黑树是近似平衡(最长路径不超过最短路径的2倍) 降低了插入和旋转的次数,
所以在经常进行增删的结构中性能比 AVL 树更优,而且红黑树实现比较简单,所以实际运用中红
黑树更多

红黑树的性质 

1. 每个结点不是 红色 就是黑色
2. 根节点 黑色  
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 ,即 不能出现连续的红节点
    父子节点:黑+黑  黑+红 红+黑
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
    即 每条路径都包含相同数量的黑节点 
5. 每个叶子结点都是黑色的 ( 此处的叶子结点指的是空结点 )
    即NIL节点,方便数路径,不容易出错

 

红黑树的插入

 新增节点的颜色默认给红色

因为新增节点若为黑色节点,插入后会影响所有路径(红黑树的性质规定每条路径必须有相同数量的黑色节点)

而新增插入红色节点只会影响父节点,(父子节点的组合:黑+黑,黑+红,红+黑)

(若父节点为黑,则无影响,若父节点为红,则有连续的红节点,需要调整,下面会讲)

红黑树节点的设计:

enum Colour
{RED,BLACK
};template<class T> // T可以是set的K,可以是map的pair<K,V> 
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)//新增节点默认给红色{}
};
红黑树是在二叉搜索树的基础上加上其平衡限制条件,故而红黑树的插入分为两步:
1 插入新增节点
2 判断新增节点插入后是否需要调整红黑树
(新增节点可能会导致连续红节点的出现,破坏了红黑树的规则)
什么时候需要调整红黑树:出现了连续的红节点,即新增节点的父节点为红色节点时
(新增节点默认为红,若父节点为黑,则没有违反红黑树的任何规则,插入完成后无需处理)
约定 :cur 为当前节点, p 为父节点, g 为祖父节点, u 为叔叔节点
红黑树的调整关键看叔叔节点
情况一 : cur 为红, p 为红, g 为黑, u存在且为红
(因为在cur插入之前,没有违反红黑树的任何规则,所以当p为红时,g一定为黑,不可能出现连续的红色节点)
解决方式 :将 p,u 改为黑, g 改为红,然后把 g 当成 cur ,继续向上调整

情况二: cur为红,p为红,g为黑,u不存在/u存在且为黑  

在这种情况下,单纯变色无法解决问题,需要旋转+变色

解决方案:旋转(单选/双旋)+变色

需要 单旋 时的情况:
p g 的左孩子, cur p 的左孩子,则进行右单旋
p g 的右孩子, cur p 的右孩子,则进行左单旋
p g 变色 -- p变黑,g变红

需要双旋时的情况:

p g 的左孩子, cur p 的右孩子,则进行左右双旋
(先对p节点所在子树左单旋,再对g节点所在子树右单旋)
p g 的右孩子, cur p 的左孩子,则进行右左双旋
(先对p节点所在子树右单旋,再对g节点所在子树左单旋)
cur,g变色-- cur变黑,g变红

代码实现:

pair<Node*, bool> Insert(const T& data){//插入一个红色节点if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(_root, true);}Node* cur = _root;Node* parent = nullptr;KeyOfT kot;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);Node* newnode = cur;if (kot(parent->_data)>kot(data)){parent->_left = cur;cur->_parent = parent;}else{parent->_right = cur;cur->_parent = parent;}//红黑树调整--有连续的红节点while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){//     g//   p   u// cNode* uncle = grandfather->_right;if (uncle && uncle->_col == RED)//uncle存在且为红--变色{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}else//uncle不存在或者存在且为黑--旋转+变色{if (cur == parent->_left)//右单旋{//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//左右双旋{//     g//   p//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else//parent == grandfather->_right{//     g//   u   p //          cNode* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//uncle存在且为红--变色{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}else//uncle不存在或者存在且为黑--旋转+变色{if (cur == parent->_right)//左单旋{RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//右左双旋{//     g//   u   p //     cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(newnode, true);}

需要用到的左单旋 右单旋:(在AVL数的代码实现中有具体讲解)

void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL){subRL->_parent = parent;}Node*parentParent = parent->_parent;parent->_parent = subR;subR->_left = parent;if (_root == parent){_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 = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = 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;}}


 红黑树的验证

1. 验证 其是否满足二叉搜索树 ( 中序遍历是否为有序序列 )
2. 验证 其是否满足红黑树的性质
bool IsBalance(){//检查根节点if (_root == nullptr)return true;if (_root->_col == RED)return false;//检查是否有连续的红节点+每条路径的黑色节点数目是否一样int refVal = 0;//参考值Node* cur = _root;while (cur)//以最左边的路径上的黑色节点数目为参考值{if (cur->_col == BLACK)refVal++;cur = cur->_left;}int blacknum = 0;return  Check(_root, refVal, blacknum);}bool Check(Node* root, const int refVal,int blacknum){if (root == nullptr){if (blacknum != refVal){cout << "存在黑色节点数量不相等的路径" << endl;return false;}return true;}if (root->_col == BLACK)//节点为黑色--统计{blacknum++;}if(root->_col == RED && root->_parent->_col == RED)//节点为红色--检查{cout << "有连续的红色节点" << endl;return false;}return Check(root->_left, refVal, blacknum)&& Check(root->_right, refVal, blacknum);}

红黑树模拟实现map与set

代码:

MyMap.h

#pragma once
#include"RBTree.h"namespace djx
{template<class K,class V>class map{public:struct MapKeyOfT//获取关键字K,map存储的是pair<K,V>{const K& operator()(const pair<K, V>&kv){return kv.first;}};// 对类模板取内嵌类型,加typename告诉编译器这里是类型typedef typename RBTree<K, pair<const K, V> ,MapKeyOfT>::iterator iterator;typedef typename RBTree<K, pair<const K, V> ,MapKeyOfT>::const_iterator const_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;//ret.first是迭代器,能够找到节点}pair<iterator, bool> insert(const pair<K, V>&kv){return _t.Insert(kv);}private:RBTree<K, pair<const K, V> ,MapKeyOfT> _t;//封装红黑树};
}

MySet.h

#pragma once
#include"RBTree.h"namespace djx
{template<class K>class set{public:struct SetKeyOfT//仿函数,返回关键字K,set存储的就是K{const K& operator()(const K& key){return key;}};typedef typename RBTree<K, K,SetKeyOfT>::const_iterator iterator;//set中的元素不可被修改,所以普通迭代器就用const_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;};
}

RBTree.h

#pragma once// set ->key
// map ->key/value
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>
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 && parent->_right == cur){cur = parent;parent = cur->_parent;}_node = parent;}return *this;}bool operator!=(const Self& s){return s._node != _node;}bool operator==(const Self& s){return s._node == _node;}
};template<class K,class T,class KeyOfT>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef __TreeIterator<T, T&, T*> iterator;typedef __TreeIterator<T, const T&, const T*> const_iterator;iterator begin()//红黑树中序序列得到有序序列,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);}//返回值不能是pair<iterator, bool>,因为set的普通迭代器实际也是const_iterator,set设计insert时要返回的pair<iterator, bool> 实际是pair<const_iterator, bool> ,而封装红黑树,复用红黑树的Insert(返回值若是pair<iterator, bool>,红黑树的普通迭代器就是普通迭代器,那么因为普通迭代器iterator不能转成const_iterator,所以代码会报错)
设计成pair<Node*, bool>就很好,节点指针Node*可以通过const_iterator迭代器的构造函数完成转变pair<Node*, bool> Insert(const T& data){//插入一个红色节点if (_root == nullptr){_root = new Node(data);_root->_col = BLACK;return make_pair(_root, true);}Node* cur = _root;Node* parent = nullptr;KeyOfT kot;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);Node* newnode = cur;if (kot(parent->_data)>kot(data)){parent->_left = cur;cur->_parent = parent;}else{parent->_right = cur;cur->_parent = parent;}//红黑树调整--有连续的红节点while (parent && parent->_col == RED){Node* grandfather = parent->_parent;if (parent == grandfather->_left){//     g//   p   u// cNode* uncle = grandfather->_right;if (uncle && uncle->_col == RED)//uncle存在且为红--变色{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}else//uncle不存在或者存在且为黑--旋转+变色{if (cur == parent->_left)//右单旋{//     g//   p// cRotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//左右双旋{//     g//   p//     cRotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}else//parent == grandfather->_right{//     g//   u   p //          cNode* uncle = grandfather->_left;if (uncle && uncle->_col == RED)//uncle存在且为红--变色{parent->_col = uncle->_col = BLACK;grandfather->_col = RED;//继续向上调整cur = grandfather;parent = cur->_parent;}else//uncle不存在或者存在且为黑--旋转+变色{if (cur == parent->_right)//左单旋{RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else//右左双旋{//     g//   u   p //     cRotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;}}}_root->_col = BLACK;return make_pair(newnode, true);}iterator Find(const K& key){//...return end();}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL){subRL->_parent = parent;}Node*parentParent = parent->_parent;parent->_parent = subR;subR->_left = parent;if (_root == parent){_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 = subL->_right;parent->_left = subLR;if (subLR)subLR->_parent = parent;Node* parentParent = parent->_parent;subL->_right = 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;}}bool IsBalance()//红黑树的验证{//检查根节点if (_root == nullptr)return true;if (_root->_col == RED)return false;//检查是否有连续的红节点+每条路径的黑色节点数目是否一样int refVal = 0;//参考值Node* cur = _root;while (cur)//以最左边的路径上的黑色节点数目为参考值{if (cur->_col == BLACK)refVal++;cur = cur->_left;}int blacknum = 0;return  Check(_root, refVal, blacknum);}bool Check(Node* root, const int refVal,int blacknum){if (root == nullptr){if (blacknum != refVal){cout << "存在黑色节点数量不相等的路径" << endl;return false;}return true;}if (root->_col == BLACK)//节点为黑色--统计{blacknum++;}if(root->_col == RED && root->_parent->_col == RED)//节点为红色--检查{cout << "有连续的红色节点" << endl;return false;}return Check(root->_left, refVal, blacknum)&& Check(root->_right, refVal, blacknum);}int Height(){return _Height(_root);}int _Height(Node* root){if (root == nullptr)return 0;int leftHeight = _Height(root->_left);int rightHeight = _Height(root->_right);return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;}size_t Size(){return _Size(_root);}size_t _Size(Node* root){if (root == nullptr)return 0;return _Size(root->_left) + _Size(root->_right) + 1;}
private:Node* _root = nullptr;
};

处理设计红黑树Insert函数返回值的细节:

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

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

相关文章

CleanMyMac X .4.14.7如何清理 Mac 系统?

细心的用户发现苹果Mac电脑越用越慢&#xff0c;其实这种情况是正常的&#xff0c;mac电脑用久了会产生很多的缓存文件&#xff0c;如果不及时清理会影响运行速度。Mac系统在使用过程中都会产生大量系统垃圾&#xff0c;如不需要的系统语言安装包&#xff0c;视频网站缓存文件&…

嵌入式-Stm32-江科大基于标准库的GPIO4个小实验

文章目录 一 、硬件介绍二 、实验&#xff1a;LED闪烁、LED流水灯、蜂鸣器提示2.1 需求1&#xff1a;面包板上的LED以1s为周期进行闪烁。亮0.5s,灭0.5s.....2.2 需求2: 8个LED实现流水灯2.3 需求3&#xff1a;蜂鸣器不断地发出滴滴、滴滴.....的提示音。蜂鸣器低电平触发。 三、…

【elementUI】el-select相关问题

官方使用DEMO <template><el-select v-model"value" placeholder"请选择"><el-optionv-for"item in options":key"item.value":label"item.label":value"item.value"></el-option></…

测试用例评审流程

1:评审的过程 A:开始前做好如下准备 1、确定需要评审的原因 2、确定进行评审的时机 3、确定参与评审人员 4、明确评审的内容 5、确定评审结束标准 6、提前至少一天将需要评审的内容以邮件的形式发送给评审会议相关人员。并注明详审时间、地点及偿参与人员等。 7、 在邮件中提醒…

P2P DMA并不是所有场景都会有性能提升

P2P (Peer-to-Peer) DMA技术理论上可以带来性能提升&#xff0c;特别是在特定的工作负载和场景下。例如&#xff0c;当两个高速设备&#xff08;如GPU与NVMe SSD&#xff09;需要频繁进行大量数据交换时&#xff0c;通过P2P DMA&#xff0c;数据可以直接在设备间传输&#xff0…

你竟然还不知道SQL性能分析?(你想象不到的详细)

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL-进阶篇 &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现…

外呼机器人有什么优势?

外呼机器人有什么优势&#xff1f;值得受到大多数电销企业的追捧&#xff01; 1、电话外呼效率高&#xff1a; 每天可拨打的电话数量是人工的5-10倍&#xff0c;人工一天只能拨打200-300通电话&#xff0c;机器人每天能打3000通电话以上&#xff0c;无须休息&#xff0c;按照…

139基于matlab多旅行商MTSP问题

基于matlab多旅行商MTSP问题&#xff0c;利用遗传算法求解多旅行商问题的算法设计&#xff0c;输出MTSP路径。相互独立路径&#xff0c;同一起点路径。程序已调通&#xff0c;可直接运行。 139 matlab多旅行熵M-TSP (xiaohongshu.com)https://www.xiaohongshu.com/explore/65ab…

云原生场景下,AIGC 模型服务的工程挑战和应对

作者&#xff1a;徐之浩、车漾 “成本”、“性能”和 “效率”正在成为影响大模型生产和应用的三个核心因素&#xff0c;也是企业基础设施在面临生产、使用大模型时的全新挑战。AI 领域的快速发展不仅需要算法的突破&#xff0c;也需要工程的创新。 大模型推理对基础设施带来…

为vs code配置unity开发环境

1.安装.NET.Core SDK 我们可以访问官网下载安装SDK及tool&#xff08;https://www.microsoft.com/net/download/core&#xff09;下载。有的系统只提供了执行文件&#xff0c;没有提供安装包&#xff0c;需要自己做一些配置。 下载好对应的版本就可以安装了&#xff0c;安装好以…

九、Qt C++ 数据库开发

《一、QT的前世今生》 《二、QT下载、安装及问题解决(windows系统)》《三、Qt Creator使用》 ​​​ 《四、Qt 的第一个demo-CSDN博客》 《五、带登录窗体的demo》 《六、新建窗体时&#xff0c;几种窗体的区别》 《七、Qt 信号和槽》 《八、Qt C 毕业设计》 《九、Qt …

递归、搜索与回溯算法(专题二:深搜)

往期文章&#xff08;希望小伙伴们在看这篇文章之前&#xff0c;看一下往期文章&#xff09; &#xff08;1&#xff09;递归、搜索与回溯算法&#xff08;专题零&#xff1a;解释回溯算法中涉及到的名词&#xff09;【回溯算法入门必看】-CSDN博客 &#xff08;2&#xff09…

TDengine 创始人陶建辉在汽车 CIOCDO 论坛发表演讲,助力车企数字化转型

当前&#xff0c;汽车行业的数字化转型如火如荼。借助数字技术的充分利用&#xff0c;越来越多的车企进一步提升了成本优化、应用敏捷性、高度弹性和效率。这一转型使得业务应用的开发和管理模式发生了颠覆性的创新&#xff0c;赋予了汽车软件快速响应变化和动态调度资源的能力…

54.螺旋矩阵(js)

题目&#xff1a; 给你一个 m 行 n 列的矩阵 matrix &#xff0c;请按照 顺时针螺旋顺序 &#xff0c;返回矩阵中的所有元素。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8,9]] 输出&#xff1a;[1,2,3,6,9,8,7,4,5] 思路&#xff1a; 先实现方向控制…

AI日报:扎克伯格瞄准AGI通用人工智能

文章目录 Meta瞄准通用人工智能领域Meta的目标Meta的产品 FAIR移动和装载H100扎克伯格对人工智能竞争对手的真实动机持怀疑态度Meta抛弃了元宇宙吗&#xff1f; Meta瞄准通用人工智能领域 Meta首席执行官马克扎克伯格&#xff08;Mark Zuckerberg&#xff09;在一份可能改变全…

Pycharm Terminal 无法激活conda环境

1.问题 Failed to activate conda environment. Please open Anaconda prompt, and run conda init powershell there. 这导致我们无法在Pycharm中使用conda命令 2.解决办法 修改为第二个&#xff0c;然后重启Terminal 再打开时发现已经是当前的conda环境

如何优化SQL查询性能?解开你的数据库瓶颈之谜!

目录 1、前言 2、创建索引 2.1 确保表的主键和外键都有索引 2.2 根据查询条件创建适当的索引 2.3 避免在索引列上进行类型转换或函数操作 3、合理设计数据库架构 3.1 表的拆分和归并&#xff0c;避免不必要的数据冗余 3.2 使用适当的数据类型和字段长度&#xff0…

linux的PXE服务(进阶知识)

一、批量部署概述 什么是PXE 预启动执行环境&#xff08;PXE&#xff09;是由Intel公司开发的最新技术&#xff0c;工作于Client/Server的网络模式&#xff0c;支持工作站通过网络从远端服务器下载映像&#xff0c;并由此支持通过网络启动操作系统&#xff0c;在启动过程中&am…

ros2仿真学习04 -turtlebot3实现cartographer算法建图演示

安装看这里 https://blog.csdn.net/hai411741962/article/details/135619608?spm1001.2014.3001.5502 虚拟机配置&#xff1a; 内存16g cpu 4 核 磁盘40G,20G 不够 启动仿真 ros2 launch turtlebot3_gazebo turtlebot3_world.launch.py启动成功如下 启动建图 重新开一个…

softmax回归

softmax回归 我们从一个图像分类问题开始。 假设每次输入是一个22的灰度图像。 我们可以用一个标量表示每个像素值&#xff0c;每个图像对应四个特征x1,x2,x3,x4。 此外&#xff0c;假设每个图像属于类别“猫”“鸡”和“狗”中的一个。 但是一般的分类问题并不与类别之间的自…