当前位置: 首页 > news >正文

c++学习之---红黑树的实现

目录

一,红黑树的概念:

二,红黑树的基本规则:

        1`规则一:

        2`规则二 :

        3`规则三 :

        4`规则四 :  

        5`四条规则达成的结果:

三,插入元素时的颜色维护:

        1`插入的新元素时:

        2`需要调整时的四个关键节点:

        3`四个关键节点的颜色

        4`节点颜色的维护:

         5`代码实现 :

四,树的性质验证 : 

        1`规则一的验证:

        2`规则二的验证:

        3`规则三的验证:

        4`规则四的验证:

        5`代码实现(类定义/插入函数/验证函数/旋转函数):

五,完整代码(头文件):


一,红黑树的概念:

        红黑树和AVL类似 , 都是搜索二叉树的变体 . 不同的是红黑树对于平衡的要求相比于AVL树更加宽松 . 

        AVL树通过严格限制每个节点的左右子树高度差不超过1,确保搜索效率稳定在O(logN)。相比之下,红黑树的约束条件较为宽松,仅要求子树高度差不超过两倍,因此在最坏情况下搜索效率为O(log2N)。但由于两者都保持对数级的复杂度,实际性能差异可以忽略。

        红黑树在实际应用中更为常见,因其对平衡性的要求相对宽松,使得插入、删除等操作的性能损耗更小。

二,红黑树的基本规则:

        1`规则一:

        红黑树的节点颜色被限定为黑或红 . 使用布尔值(true/false)或者定义一个仅包含黑/红两种成员的枚举类型来确保颜色取值.

        2`规则二 :

        对于非空树,根节点只能是黑色 . 一条硬性要求

        3`规则三 :

        红色节点的子节点只能是黑色节点 . 这意味着不能有两个同为红色的节点相连.

        4`规则四 :  

        每条简单路径上的黑色节点数量必须相同 . 这一点也限定了插入的新节点只能是红色 .                  

        5`四条规则达成的结果:

        节点的左右子树高度相差不超过两倍 . 下图是一种极端情况 , 当后续插入时 : 如果插入红节点 ,产生颜色冲突,通过调整后会降低右侧的高度 ; 不可能插入黑节点 , 因为会直接破坏规则四 , 让左右黑色节点数量不一致.

三,插入元素时的颜色维护:

        1`插入的新元素时:

  • 由于不能破坏规则四(每个节点两条路径上的黑色节点必须相同) , 新插入的节点只能是红色 .
  • 如果插入在黑色节点的下面 , 不用调整 ;
  • 如果插入在红色节点下面 , 就需要通过调整来维护规则三(不能出现连续红节点).

        2`需要调整时的四个关键节点:

  1. 新插入的节点 : 即刚刚插入的红色节点.
  2. 父节点 : 指的是新节点的父节点 .
  3. 祖父节点 : 指的是父节点的父节点 .
  4. 叔叔节点 : 指的是父节点的另一个子节点 

        3`四个关键节点的颜色

  1. 新插入的节点 : 为红色,不用多说.
  2. 父节点 : 只可能为红色 , 因为这样才会和新插入的红节点冲突 , 引发调整.
  3. 祖父节点 : 只可能为黑色 , 否则早就会和红色的父节点冲突了.
  4. 叔叔节点 :   有三种情况 ---                                                                                                                                                  第一种是存在且为红                                                                                                                  第二种是不存在                                                                                                                          第三种是存在且为黑 

可以看到四个节点里有三个的颜色都是固定的 , 剩下的一个叔叔节点的三种情况里其实又只有第一种是需要特别关注.

        4`节点颜色的维护:

        节点颜色的维护严格来说又分为两大类 , : 一类是新插入的节点在祖父节点的父节点的左子树,另一类自然就在右子树了 ;

        由于颜色的维护涉及旋转 , 所以这两类情况会导致旋转的类型不同 , 但颜色调整的思路殊途同归 , 下面拿第一类来举例 :

  1. 叔叔节(uncle)点存在且为红色 : 不用旋转 , 只需将父节点(Parent)和叔叔节点(uncle)设置为黑色 , 接着把祖父节点设置为红色 , 就可以解决父节点和新插入节点颜色冲突的问题 . 但是 , 由于祖先节点原来是黑的 , 所以把他设置为红色后有可能和他的父节点冲突 , 因此需要继续向上调整 . 
  2. 新插入的节点是父节点的左子树 : 需要单旋 , 以祖父节点(grandParent)为旋转点进行右单旋 --- 此时新节点和组父节点成为了父节点的子节点 . 接着将父节点调整为黑色 , 组父节点调整为红色 . 此时变成黑色的父节点是最靠上的 , 再往上的节点不论是黑是红都不会冲突,补充向上调整 ; 另外 , 这里的调整直接不用管叔叔节点了!!!
  3. 新插入的节点是父节点的右子树 : 需要双旋 , 以父节点为旋转点进行左单旋 , 再以组父节点为旋转点进行右单旋 --- 此时父节点和祖父节点成了新节点的子节点. 接着将子节点调整为黑色 , 祖父节点调整为红色即可 . 同样的 , 此时变成黑色的新节点是最靠上的 , 不用向上调整 ; 并且 , 叔叔节点还是没有起到作用.

         5`代码实现 :

bool Insert(Node* item)
{//此处略去插入新节点的代码 , newNode是新节点 , parent是他的父节点
//....................................................................................//判断颜色
cur = newNode;
while (parent != nullptr && parent->_color == false) //parent节点不为空且颜色为红才需要调整
{Node* grandParent = parent->_parent;//大情况一:新节点插入在祖父节点的的左子树if (grandParent->_left == parent){Node* uncle = grandParent->_right;if (uncle != nullptr && uncle->_color == false){grandParent->_color = false;parent->_color = true;uncle->_color = true;//更新cur = grandParent;parent = cur->_parent;}else{if (parent->_right == cur){RotateL(parent);RotateR(grandParent);cur->_color = true;grandParent->_color = false;}else{RotateR(grandParent);grandParent->_color = false;parent->_color = true;}break;}}//大情况一:新节点插入在祖父节点的的右子树else {Node* uncle = grandParent->_left;if (uncle != nullptr && uncle->_color == false){grandParent->_color = false;parent->_color = true;uncle->_color = true;//更新cur = grandParent;parent = cur->_parent;}else{if (parent->_left == cur){RotateR(parent);RotateL(grandParent);cur->_color = true;grandParent->_color = false;}else{RotateL(grandParent);grandParent->_color = false;parent->_color = true;}break;}}_root->_color = true;
}
return true;}

四,树的性质验证 : 

验证红黑树的正确性从他的四个规则入手即可.

        1`规则一的验证:

        红黑树的节点颜色被限定为黑或红

  这一点从红黑树节点的结构设计上就可以保证 , 比如我这里将节点的Color变量定义为只有两种取值的bool类型 , true为黑 , false为红.

        2`规则二的验证:

        对于非空树,根节点只能是黑色

  这一点验证起来很简单 , 判断一下根节点的颜色即可.

        3`规则三的验证:

        红色节点的子节点只能是黑色节点

  这一点验证起来需要转换思路 , 因为一个红色节点的子节点有多重情况 , 要对两个子节点都判断 .所以逆向思维 , 直接对红色节点的父节点判断即可 : 只要红色节点的父节点吧不为红即可 .

        4`规则四的验证:

        每条简单路径上的黑色节点数量必须相同 .

    如果是硬生生算出所以每条简单路径上的黑色节点数量 , 会很麻烦 .  所以使用反证法 : 假设每条简单路径上的黑色节点数量就是相同的 , 那先算出任意一条简单路径的黑色节点数量 , 再拿着这个结果作为基准值去和所有简单路径上的黑色节点数量作比对即可.

        5`代码实现(类定义/插入函数/验证函数/旋转函数):

//验证红黑树  (作为共有成员函数)
bool BlanceTree()
{if (_root == nullptr)return true;if (_root->_color == false)return false;Node* cur = _root;int BlackCount = 0;//以一条简单路径上的黑色节点作为基准值while (cur){if (cur->_color == true)BlackCount += 1;cur = cur->_left;}return SingleTrace(_root, BlackCount, 0);
}
//判断简单路径的黑色节点 (涉及递归,作为私有成员函数,专门BlanceTree使用)
bool SingleTrace(Node* root , int base , int tmp)
{//一条简单路径走完,比对基准值的黑节点数量if (root == nullptr){if (tmp != base)return false;elsereturn true;}//探测连续的红节点if (root->_color == false && root->_parent->_color == false)return false;//为黑节点则加加if (root->_color == true)tmp += 1;return SingleTrace(root->_left, base, tmp) && SingleTrace(root->_right, base, tmp);
}

五,完整代码(头文件):

#pragma once
using namespace std;template<class KEY , class VAL>
struct BRNode
{//构造函数BRNode(pair<KEY,VAL> item):_item(item){_parent = _left = _right = nullptr;_color = true;}//成员变量pair<KEY, VAL> _item;BRNode < KEY, VAL >* _parent;BRNode < KEY, VAL >* _left;BRNode < KEY, VAL >* _right;bool _color; //true为黑,false为红
};template<class KEY, class VAL>
class BRTree
{
public:using Node = BRNode<KEY, VAL>;//插入元素bool Insert(pair<KEY, VAL> input){//空树if (nullptr == _root){_root = new Node(input);}//寻找插入位置Node* cur = _root;Node* parent = nullptr;while (cur){if (input.first < cur->_item.first){parent = cur;cur = cur->_left;}else if (input.first > cur->_item.first){parent = cur;cur = cur->_right;}else{return false;}}//插入新节点Node* newNode = new Node(input);if (input.first < parent->_item.first)parent->_left = newNode;elseparent->_right = newNode;newNode->_parent = parent;newNode->_color = false;   //非根节点无脑为红//判断颜色cur = newNode;while (parent != nullptr && parent->_color == false){Node* grandParent = parent->_parent;if (grandParent->_left == parent){Node* uncle = grandParent->_right;if (uncle != nullptr && uncle->_color == false){grandParent->_color = false;parent->_color = true;uncle->_color = true;//更新cur = grandParent;parent = cur->_parent;}else{if (parent->_right == cur){RotateL(parent);RotateR(grandParent);cur->_color = true;grandParent->_color = false;}else{RotateR(grandParent);grandParent->_color = false;parent->_color = true;}break;}}else{Node* uncle = grandParent->_left;if (uncle != nullptr && uncle->_color == false){grandParent->_color = false;parent->_color = true;uncle->_color = true;//更新cur = grandParent;parent = cur->_parent;}else{if (parent->_left == cur){RotateR(parent);RotateL(grandParent);cur->_color = true;grandParent->_color = false;}else{RotateL(grandParent);grandParent->_color = false;parent->_color = true;}break;}}_root->_color = true;}return true;}//右单旋void RotateR(Node* point){Node* subL = point->_left;Node* subLR = subL->_right;point->_left = subLR;if (subLR != nullptr)subLR->_parent = point;Node* oldParent = point->_parent;subL->_right = point;point->_parent = subL;if (point == _root){_root = subL;subL->_parent = nullptr;}else{if (oldParent->_left == point)oldParent->_left = subL;elseoldParent->_right = subL;subL->_parent = oldParent;}}//左单旋void RotateL(Node* point){Node* subR = point->_right;Node* subRL = subR->_left;point->_right = subRL;if (subRL != nullptr)subRL->_parent = point;Node* oldParent = point->_parent;subR->_left = point;point->_parent = subR;if (point == _root){_root = subR;subR->_parent = nullptr;}else{if (oldParent->_left == point)oldParent->_left = subR;elseoldParent->_right = subR;subR->_parent = oldParent;}}//中序遍历void MidTrave(){_MidTrave(_root);cout << endl;}//验证红黑树bool BlanceTree(){if (_root == nullptr)return true;if (_root->_color == false)return false;Node* cur = _root;int BlackCount = 0;while (cur){if (cur->_color == true)BlackCount += 1;cur = cur->_left;}return SingleTrace(_root, BlackCount, 0);}
private://中序遍历void _MidTrave(Node* root){if (root == nullptr)return;_MidTrave(root->_left);cout << root->_item.first << "->" << root->_item.second << endl;_MidTrave(root->_right);}//判断简单路径的黑色节点bool SingleTrace(Node* root , int base , int tmp){//一条简单路径走完,比对基准值的黑节点数量if (root == nullptr){if (tmp != base)return false;elsereturn true;}//探测连续的红节点if (root->_color == false && root->_parent->_color == false)return false;//为黑节点则加加if (root->_color == true)tmp += 1;return SingleTrace(root->_left, base, tmp) && SingleTrace(root->_right, base, tmp);}Node* _root = nullptr;  //根节点初始化为空很关键
};

http://www.dtcms.com/a/277261.html

相关文章:

  • CentOS 7 升级系统内核级库 glibc 2.40 完整教程
  • MSVCP*.dll、vcruntime*.dll缺失或损坏,以及.NET Framework相关问题,解决办法
  • 移动端设备本地部署大语言模型(LLM)
  • 【论文阅读】基于注意力机制的冥想脑电分类识别研究(2025)
  • LabVIEW智能避障小车
  • C/C++数据结构之多维数组
  • vue3 el-select默认选中
  • Java_Springboot技术框架讲解部分(二)
  • 【Linux内核模块】模块加载函数--从启动到运行的幕后推手
  • MySQL 分表功能应用场景实现全方位详解与示例
  • 算法学习笔记:19.牛顿迭代法——从原理到实战,涵盖 LeetCode 与考研 408 例题
  • 先“跨栏”再上车 公交站台装70厘米高护栏 公司回应
  • Mock 数据的生成与使用全景详解
  • 知识蒸馏:模型压缩与知识迁移的核心引擎
  • 通过同态加密实现可编程隐私和链上合规
  • GraphRAG:融合知识图谱与RAG的下一代信息检索框架
  • 【RK3568 平台I2C协议与AGS10驱动开发】
  • 深度学习16(对抗生成网络:GAN+自动编码器)
  • Vue单文件组件与脚手架工程化开发
  • 【数据结构】图 ,拓扑排序 未完
  • 弹性布局详解
  • mmap映射文件
  • 【设计模式】命令模式 (动作(Action)模式或事务(Transaction)模式)宏命令
  • 【STM32实践篇】:F407 时钟系统
  • fiddler/charles https配置完毕依然无法抓取APP https请求的解决办法
  • h() 函数
  • 【RA-Eco-RA6E2-64PIN-V1.0 开发板】ADC 电压的 LabVIEW 数据采集
  • Excel的学习
  • 如何选择合适的AI论文写作工具?七个AI英文论文写作网站
  • leetGPU解题笔记(2)