C++_红黑树的学习

1. 红黑树的概念

红黑树 ,是一种 二叉搜索树 ,但 在每个结点上增加一个存储位表示结点的颜色,可以是 Red Black 。 通过对 任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路 径会比其他路径长出俩倍 ,因而是 接近平衡

2. 红黑树的性质

1. 每个结点不是红色就是黑色
2. 根节点是黑色的 
3. 如果一个节点是红色的,则它的两个孩子结点是黑色的 
4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点 
5. 每个叶子结点都是黑色的 ( 此处的叶子结点指的是空结点 )

3. 红黑树节点的定义

新增结点给红色,因为给黑色会破坏各个路径黑色结点数量相同的条件

// 节点的颜色
enum Color { RED, BLACK };
// 红黑树节点的定义
template<class ValueType>
struct RBTreeNode
{RBTreeNode(const ValueType& data = ValueType(),Color color = RED): _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr), _data(data), _color(color){}RBTreeNode<ValueType>* _pLeft;   // 节点的左孩子RBTreeNode<ValueType>* _pRight;  // 节点的右孩子RBTreeNode<ValueType>* _pParent; // 节点的双亲(红黑树需要旋转,为了实现简单给出该字段)ValueType _data;            // 节点的值域Color _color;               // 节点的颜色
};

4. 红黑树的插入操作

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
        1. 按照二叉搜索的树规则插入新节点
        2. 检测新节点插入后,红黑树的性质是否造到破坏并更新
因为 新节点的默认颜色是红色 ,因此:如果 其双亲节点的颜色是黑色,没有违反红黑树任何 性质 ,则不需要调整;但 当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连 在一起的红色节点,此时需要对红黑树分情况来讨论:
约定 :cur 为当前节点, p 为父节点, g 为祖父节点, u 为叔叔节点

4.1 cur为红,p为红,g为黑,u存在且为红

这个时候,p与g的左右关系没有影响,只有g是否为根有影响:
如果g是根结点,调整完后,需要将g更改为黑色
如果g是子树,g就一定有双亲,且如果g的双亲如果是红色,则需要继续向上调整

解决方式:将 p,u 改为黑, g 改为红,然后把 g 当成 cur ,继续向上调整

4.2 cur为红,p为红,g为黑,u不存在/u存在且为黑

这个时候,p与g的左右关系有影响:

这时有两种情况:
1. u 结点不存在,则cur 一定是新增结点,因为如果cur不是新增结点,则cur和p一定有一个结点的颜色是黑色,不然就破坏了各路径黑色结点数量相等的条件,但是这与更新条件相违背:只有c和p都是红色才进行更新

2.u 结点存在且为黑,则cur原来一定是黑色的,只是因为cur在的子树在先前已经更新完了,cur颜色由黑色改成红色

解决方式:p为g的左孩子,curp的左孩子,则进行右单旋转;
                  p为g的左孩子,curp的右孩子,则进行左右双旋转;相反,
                  p为g的右孩子,curp的右孩子,则进行左单旋转;
                  p为g的右孩子,cur为p的左孩子,则进行右左双旋转;
                  pg变色--p变黑,g变红
	bool Insert(const pair<K, V>& kv){//先找插入的位置if (_root == nullptr) {_root = new Node(kv);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = nullptr;while (cur){if (cur->_kv.first < kv.first){parent = cur;cur = cur->_right;}else if (cur->_kv.first > kv.first){parent = cur;cur = cur->_left;}else{return false;}}//找到后插入并连接cur和parentcur = new Node(kv);if (parent->_kv.first < kv.first)parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;//当parent存在且为红色时,需要更新颜色while (parent && parent->_col == RED){Node* grandfather = parent->_parent;//uncle是grandfather的右孩子if (grandfather->_left == parent){Node* uncle = grandfather->_right;//uncle存在且为红色if (uncle && uncle->_col == RED){//颜色更新uncle->_col = parent->_col = BLACK;grandfather->_col = RED;//继续向上遍历cur = grandfather;parent = cur->_parent;}else{//uncle不存在,或者是uncle存在但为黑色if (cur == parent->_left){//       g//    p    u// c//需要进行右单旋,更改颜色:parent->黑色,//grandfather->红色RotateR(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//       g             c//    p     u  ->   p     g//      c                    u//需要进行左右双旋,更改颜色:cur->黑色,//grandfather->红色RotateL(parent);RotateR(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;//旋转后重新平衡,直接退出}}//uncle是grandfather的左孩子else{Node* uncle = grandfather->_left;//uncle存在且为红色if (uncle && uncle->_col == RED){//颜色更新uncle->_col = parent->_col = BLACK;grandfather->_col = RED;//继续向上遍历cur = grandfather;parent = cur->_parent;}else{//uncle不存在,或者是uncle存在但为黑色if (cur == parent->_right){//       g//    u    p//           c//需要进行左单旋,更改颜色:parent->黑色,//grandfather->红色RotateL(grandfather);parent->_col = BLACK;grandfather->_col = RED;}else{//       g             c//    u     p  ->   g     p//        c       u          //需要进行右左双旋,更改颜色:cur->黑色,//grandfather->红色RotateR(parent);RotateL(grandfather);cur->_col = BLACK;grandfather->_col = RED;}break;//旋转后重新平衡,直接退出}}}//对根结点统一更改颜色为黑色_root->_col = BLACK;return true;}void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;parent->_right = subRL;if (subRL)subRL->_parent = parent;subR->_left = parent;Node* ppnode = parent->_parent;parent->_parent = subR;if (parent == _root){_root = subR;subR->_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;subL->_right = parent;Node* ppnode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{if (ppnode->_left == parent){ppnode->_left = subL;}else{ppnode->_right = subL;}subL->_parent = ppnode;}}

5. 红黑树的验证

红黑树的检测分为两步:
1. 检测其是否满足二叉搜索树 ( 中序遍历是否为有序序列 )
2. 检测其是否满足红黑树的性质
	bool Check(Node* cur, int blackNum, int refBlackNum){//在一条路径走完后再判定黑色结点的数量是否有异常,有就报错//什么时候走完? 当前结点走到空结点就走完一条路径,这里用前序遍历if (cur == nullptr){if (blackNum != refBlackNum){cout << "黑色结点数量异常,错误!" << endl;cout << blackNum << endl;return false;}//cout << blackNum << endl;return true;}//有连续的红色结点就报错:找到一个红结点再看它的parent是不是红结点if (cur->_col == RED && cur->_parent && cur->_parent->_col == RED){cout << "有连续的红结点,错误!" << endl;return false;}//遇到黑色结点,blackNum++if (cur->_col == BLACK)blackNum++;return Check(cur->_left, blackNum, refBlackNum)&& Check(cur->_right, blackNum, refBlackNum);}bool IsBalance(){//空结点也是红黑树if (_root == nullptr)return true;//根存在但是根的颜色是红就报错if (_root && _root->_col == RED){cout << "根是红色,错误!" << endl;return false;}//先遍历最左路径,得到黑色结点的数量int refBlackNum = 0;Node* cur = _root;while (cur){if (cur->_col == BLACK)refBlackNum++;cur = cur->_left;}return Check(_root, 0, refBlackNum);}

6. 红黑树的模拟实现

#pragma once
#include<vector>
#include<iostream>
using namespace std;enum Colour
{RED,BLACK
};template<class K, class V>
struct RBTreeNode
{RBTreeNode<K, V>* _left;RBTreeNode<K, V>* _right;RBTreeNode<K, V>* _parent;pair<K, V> _kv;Colour _col;RBTreeNode(const pair<K, V>& kv):_left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _col(RED)//如果是根,则为黑色,新增结点默认是红色{}
};template<class K, class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:bool Insert(const pair<K, V>& kv){......}void RotateL(Node* parent){......}void RotateR(Node* parent){......}bool Check(Node* cur, int blackNum, int refBlackNum){......}bool IsBalance(){......}Node* Find(const K& key){Node* cur = _root;while (cur){if (cur->_kv.first < key){cur = cur->_right;}else if (cur->_kv.first > key){cur = cur->_left;}else{return cur;}}return NULL;}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;}int Height(){return _Height(_root);}private:Node* _root = nullptr;
};

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

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

相关文章

文本检测模型 DBNet 一种基于分割算法的模型 对每个像素点进行自适应二值化,并将二值化过程与网络训练相结合 可微分二值化模块 概率图

文本检测模型 DBNet DBNet文本检测模型是一种基于分割算法的模型,其优化之处在于对每个像素点进行自适应二值化,并将二值化过程与网络训练相结合。 传统的文本检测方法通常将二值化作为一个后处理步骤,与网络训练分开进行。而DBNet则提出了一种可微分的二值化方法,即将文…

Docker需要代理下载镜像

systemctl status docker查看docker的状态和配置文件是/usr/lib/systemd/system/docker.service vi /usr/lib/systemd/system/docker.service&#xff0c; 增加如下配置项 [Service] Environment"HTTP_PROXYhttp://proxy.example.com:8080" "HTTPS_PROXYhttp:…

【机器学习】集成学习在信用评分领域实例

集成学习在信用评分领域的应用与实践 一、引言二、集成学习的概念与原理三、集成学习在信用评分中的应用实例四、总结与展望 一、引言 在当今金融数字化快速发展的时代&#xff0c;信用评分成为银行、金融机构等评估个人或企业信用风险的重要工具。然而&#xff0c;单一的信用评…

欢乐钓鱼大师自动钓鱼,游戏辅助!

在探索《欢乐钓鱼大师》的世界时&#xff0c;一项备受关注的功能是陀螺仪模式。这是一种利用手机陀螺仪传感器来增强游戏体验的功能&#xff0c;通过模拟真实的钓鱼动作&#xff0c;让玩家更深入地沉浸在游戏的世界中&#xff0c;感受到更加逼真的钓鱼体验。在本篇攻略中&#…

感知机和神经网络

引入 什么是神经网络&#xff1f; 我们今天学习的神经网络&#xff0c;不是人或动物的神经网络&#xff0c;但是又是模仿人和动物的神经网络而定制的神经系统&#xff0c;特别是大脑和神经中枢&#xff0c;定制的系统是一种数学模型或计算机模型&#xff0c;神经网络由大量的人…

【iOS开发】—— 初识锁

【iOS开发】—— 初识锁 线程安全锁的种类自旋锁定义原理自旋锁缺点OSSpinLock&#xff08;自旋锁&#xff09; 互斥锁os_unfair_lockpthread_mutexNSLockNSRecusiveLockSemaphore信号量synchronized 总结两种之间的区别和联系&#xff1a; 线程安全 当一个线程访问数据的时候…

【微服务】spring aop实现接口参数变更前后对比和日志记录

目录 一、前言 二、spring aop概述 2.1 什么是spring aop 2.2 spring aop特点 2.3 spring aop应用场景 三、spring aop处理通用日志场景 3.1 系统日志类型 3.2 微服务场景下通用日志记录解决方案 3.2.1 手动记录 3.2.2 异步队列es 3.2.3 使用过滤器或拦截器 3.2.4 使…

Windows环境下编译 aom 源码详细过程

AV1 AV1是一种开源的视频编码格式&#xff0c;由开放媒体联盟&#xff08;AOMedia Video 1&#xff0c;简称AOMedia或AOM&#xff09;开发。AV1旨在提供比现有的视频编码格式如H.264和H.265更好的压缩效率&#xff0c;同时保持或提高视频质量。AV1的编码效率显著高于H.264&…

差分约束 C++ 算法例题

差分约束 差分约束 是一种特殊的 n 元一次不等式组&#xff0c;m 个约束条件&#xff0c;可以组成形如下的格式&#xff1a; { x 1 − x 1 ′ ≤ y 1 x 2 − x 2 ′ ≤ y 2 ⋯ x m − x m ′ ≤ y m \begin{cases} x_1-x_1^{} \le y_1 \\ x_2-x_2^{} \le y_2 \\ \cdots \\ x_…

【机器学习】 技术栈和开发环境搭建

各位大佬好 &#xff0c;这里是阿川的博客 &#xff0c; 祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 博客目录 技术栈编程语言库框架编辑器项目IDE …

行业分析---马斯克的Tesla

1 背景 在前面的博文《行业分析---我眼中的Apple Inc.》中&#xff0c;笔者曾介绍过苹果公司的财报和商业。依然本着提升自己看公司的能力&#xff0c;尝试去分析相对熟悉的公司&#xff0c;看懂它的商业。在之前的博客《自动驾驶---Tesla之FSD简介》中&#xff0c;笔者也简单介…

UE5C++ FString做为参数取值时报错error:C4840

问题描述 用来取FString类型的变量时报错&#xff1a; 问题解决 点击错误位置&#xff0c;跳转到代码&#xff1a; void AMyDelegateActor::TwoParamDelegateFunc(int32 param1, FString param2) {UE_LOG(LogTemp, Warning, TEXT("Two Param1:%d Param2:%s"), param…