红黑树(RBTree)的插入算法以及如何测试一棵树是否是红黑树?(详细图解说明)

本文主要是介绍红黑树(RBTree)的插入算法以及如何测试一棵树是否是红黑树?(详细图解说明),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.什么叫红黑树?

             红黑树是一棵二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是Red或Black。通过对任何一条从根到叶子简单路径上的颜色来约束,红黑树保证最长路径不超过最短路径的两倍,因而近似于平衡。


2.红黑树的性质

     

   1. 每个节点,不是红色就是黑色的

   2. 根节点是黑色的

   3. 如果一个节点是红色的,则它的两个子节点是黑色的(没有连续的红色结点

   4. 对每个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。(每条路径的黑色结点个数相等)

   5. 每个叶子节点都是黑色的(这里的叶子节点是指的NIL节点(空节点))


3.红黑树为什么通过颜色的约束能够保证最长路径不超过最短路径的两倍?




4.红黑树的结点的定义

enum Colour
{RED,BLACK
};template<typename K,typename V>
struct RBTreeNode
{//结点RBTreeNode* _left;RBTreeNode* _right;RBTreeNode* _parent;//颜色Colour _col;//KVK _key;V _value;RBTreeNode(const K& key, const V& value):_key(key), _value(value), _left(NULL), _right(NULL), _parent(NULL), _col(RED)  //把新增结点置成红色为了保持每条路径的黑色结点个数不变{}
};


为什么把新增结点的颜色设置为红色而不是黑色呢?

    因为设置为红色,比较容易维护这棵树,设置为黑色,会导致当前这条路径多了一个黑色结点,为了满足每条路径的黑色结点个数相同,需要维护多条路径。



5.红黑树插入的几种情况


注:由于p结点(父结点)可能为g结点(祖父结点)的左孩子或者右孩子,下面的作图以左孩子为模板,右孩子与左孩子类似,只是在某些需要旋转的情况下不一样。

    (1)插入结点为根节点(只有一个结点的情况),将结点颜色置成黑色即可

    

    (2)插入结点的父结点或调整结点的父结点为黑色结点(不需要调整)



  

  (3)插入结点的父结点或向上调整的结点父结点为红色之——uncle结点存在为红色

  

  (4)插入结点的父结点为红色之——uncle结点不存在



  (5)插入结点的父结点为红色之——uncle结点存在为黑色





6.红黑树插入实现代码


template<class K,class V>
class RBTree
{typedef RBTreeNode<K, V> Node;
public:RBTree():_root(NULL){}bool Insert(const K& key, const V& value){//1.插入结点为根节点,必须为黑色if (_root == NULL){_root = new Node(key, value);_root->_col = BLACK;return true;}Node* cur = _root;Node* parent = NULL;while (cur != NULL){if (cur->_key > key){parent = cur;cur = cur->_left;}else if (cur->_key < key){parent = cur;cur = cur->_right;}else{return false;}}cur = new Node(key, value);if (key > parent->_key)parent->_right = cur;elseparent->_left = cur;cur->_parent = parent;while (parent != NULL){Node* pparent = parent->_parent;Node* uncle = NULL;//2.如果插入节点或者调整结点的父结点为黑色结点,增加一个红色结点,对每条路径的黑色结点个数没影响if (parent->_col == BLACK){break;}//判断叔叔结点是否存在,存在是在左还是右?if (pparent != NULL){if (pparent->_right == parent)uncle = pparent->_left;elseuncle = pparent->_right;}//3.插入结点或调整结点父结点为红色且叔叔结点为红色if (uncle != NULL&&uncle->_col == RED){parent->_col = BLACK;uncle->_col = BLACK;if (pparent == _root)return true;pparent->_col = RED;//继续向上调整//假如调整到根结点,只需将根结点的颜色置为黑色即可if (parent == _root){parent->_col = BLACK;return true;}cur = parent->_parent;parent = cur->_parent;}//4.插入结点或调整结点父结点为红色且叔叔结点不存在或//  插入结点或调整结点父结点为红色且叔叔结点存在为黑色二者逻辑一样。else//剩下的情况 (uncle == NULL || uncle->_col == BLACK){//判断父结点在左还是在有if (pparent->_left == parent){//左--左if (parent->_left == cur){_RotateR(pparent);pparent->_col = RED;parent->_col = BLACK;if (pparent == _root){_root = parent;}}//左--右else{_RotateL(parent);_RotateR(pparent);cur->_col = BLACK;parent->_col = RED;pparent->_col = RED;if (pparent == _root)_root = cur;}}//父结点在右else{//右--右if (parent->_right == cur){_RotateL(pparent);pparent->_col = RED;parent->_col = BLACK;if (pparent == _root)_root = parent;}//右--左else{_RotateR(parent);_RotateL(pparent);parent->_col = RED;pparent->_col = RED;cur->_col = BLACK;if (pparent == _root)_root = parent;}}break;}}return true;}void InOrder(){_InOrder(_root);cout << endl;}
protected:void _InOrder(Node* root){if (root == NULL)return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}void _RotateR(Node* parent){Node* SubL = parent->_left;Node* SubLR = SubL->_right;//链上SubLRparent->_left = SubLR;if (SubLR != NULL)SubLR->_parent = parent;Node* pparent = parent->_parent;//链上SubLSubL->_parent = pparent;if (pparent != NULL){if (pparent->_left == parent)pparent->_left = SubL;elsepparent->_right = SubL;}//链上parentparent->_parent = SubL;SubL->_right = parent;}void _RotateL(Node* parent){Node* SubR = parent->_right;Node* SubRL= SubR->_left;//链上SubLRparent->_right= SubRL;if (SubRL != NULL)SubRL->_parent = parent;Node* pparent = parent->_parent;//链上SubLSubR->_parent = pparent;if (pparent != NULL){if (pparent->_left == parent)pparent->_left = SubR;elsepparent->_right = SubR;}//链上parentparent->_parent = SubR;SubR->_left = parent;}Node* _root;
};

7.如何测试一棵树是不是红黑树?

(1)测试它的根节点是不是黑色

(2)测试是否有连续的红结点

(3)测试每条路径上的黑节点的数量是否相等?

以某一条路径(测试用例用的是左子树)上的黑色结点的个数为判断标准,如果有一条不满足相等,证明不是红黑树。

实现代码:

    

bool Isbalance(){//1.空树认为是RBTreeif (_root == NULL)return true;//2.根结点非黑不是RBTreeif (_root->_col == RED)return false;int count = 0;//以左子树上的黑色结点作为一个基准,如果一样就证明是,不一样就证明不是RBTreeNode* cur = _root;while (cur != NULL){if (cur->_col == BLACK)count++;cur = cur->_left;}//num用来标记每条路径上的黑色结点的个数//count用来标记左子树上的黑色结点的个数int num = 0;return _Isbalance(_root, count, num);}bool _Isbalance(Node* root, int count, int num){if (root == NULL)return true;//3.连续红结点不是RBTree,root结点一定有父结点if (root->_col == RED&&root->_parent->_col == RED){cout << root->_key << " 有连续的红结点" << endl;return false;}if (root->_col == BLACK)num++;if (root->_left == NULL&&root->_right == NULL){if (num != count){cout << root->_key << " 黑色结点个数不一样" << endl;return false;}}return _Isbalance(root->_left, count, num) && _Isbalance(root->_right, count, num);}

  




8.测试用例

测试用例:

void TestRBTree()
{int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };RBTree<int, int> t;for (size_t i = 0; i < sizeof(a) / sizeof(a[0]); i++){t.Insert(a[i], i);}t.InOrder();cout << "Isbalance---->" << t.Isbalance()<< endl;
}


       运行结果和测试用例红黑树模型:





9.红黑树和AVL树比较

      (1)红黑树和AVL树都是高效的平衡二叉树,增删查改的时间复杂度都是O(lg(N))。            

         (2)红黑树的不追求完全平衡,保证最长路径不超过最短路径的2倍,相对而言,降低了旋转的要求,所以性能会优于AVL树,所以实际运用中红黑树更多。(效率差不多要求还低)。

注:

AVL旋转因子的调节(详细图解)



这篇关于红黑树(RBTree)的插入算法以及如何测试一棵树是否是红黑树?(详细图解说明)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/579925

相关文章

Goland debug失效详细解决步骤(合集)

《Golanddebug失效详细解决步骤(合集)》今天用Goland开发时,打断点,以debug方式运行,发现程序并没有断住,程序跳过了断点,直接运行结束,网上搜寻了大量文章,最后得以解决,特此在这... 目录Bug:Goland debug失效详细解决步骤【合集】情况一:Go或Goland架构不对情况二:

Python itertools中accumulate函数用法及使用运用详细讲解

《Pythonitertools中accumulate函数用法及使用运用详细讲解》:本文主要介绍Python的itertools库中的accumulate函数,该函数可以计算累积和或通过指定函数... 目录1.1前言:1.2定义:1.3衍生用法:1.3Leetcode的实际运用:总结 1.1前言:本文将详

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

Spring Boot整合log4j2日志配置的详细教程

《SpringBoot整合log4j2日志配置的详细教程》:本文主要介绍SpringBoot项目中整合Log4j2日志框架的步骤和配置,包括常用日志框架的比较、配置参数介绍、Log4j2配置详解... 目录前言一、常用日志框架二、配置参数介绍1. 日志级别2. 输出形式3. 日志格式3.1 PatternL

Springboot 中使用Sentinel的详细步骤

《Springboot中使用Sentinel的详细步骤》文章介绍了如何在SpringBoot中使用Sentinel进行限流和熔断降级,首先添加依赖,配置Sentinel控制台地址,定义受保护的资源,... 目录步骤 1: 添加 Sentinel 依赖步骤 2: 配置 Sentinel步骤 3: 定义受保护的

Spring Boot Actuator使用说明

《SpringBootActuator使用说明》SpringBootActuator是一个用于监控和管理SpringBoot应用程序的强大工具,通过引入依赖并配置,可以启用默认的监控接口,... 目录项目里引入下面这个依赖使用场景总结说明:本文介绍Spring Boot Actuator的使用,关于Spri

使用Python在Excel中插入、修改、提取和删除超链接

《使用Python在Excel中插入、修改、提取和删除超链接》超链接是Excel中的常用功能,通过点击超链接可以快速跳转到外部网站、本地文件或工作表中的特定单元格,有效提升数据访问的效率和用户体验,这... 目录引言使用工具python在Excel中插入超链接Python修改Excel中的超链接Python

本地私有化部署DeepSeek模型的详细教程

《本地私有化部署DeepSeek模型的详细教程》DeepSeek模型是一种强大的语言模型,本地私有化部署可以让用户在自己的环境中安全、高效地使用该模型,避免数据传输到外部带来的安全风险,同时也能根据自... 目录一、引言二、环境准备(一)硬件要求(二)软件要求(三)创建虚拟环境三、安装依赖库四、获取 Dee

MySql9.1.0安装详细教程(最新推荐)

《MySql9.1.0安装详细教程(最新推荐)》MySQL是一个流行的关系型数据库管理系统,支持多线程和多种数据库连接途径,能够处理上千万条记录的大型数据库,本文介绍MySql9.1.0安装详细教程,... 目录mysql介绍:一、下载 Mysql 安装文件二、Mysql 安装教程三、环境配置1.右击此电脑

Git提交代码详细流程及问题总结

《Git提交代码详细流程及问题总结》:本文主要介绍Git的三大分区,分别是工作区、暂存区和版本库,并详细描述了提交、推送、拉取代码和合并分支的流程,文中通过代码介绍的非常详解,需要的朋友可以参考下... 目录1.git 三大分区2.Git提交、推送、拉取代码、合并分支详细流程3.问题总结4.git push