红黑树(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

相关文章

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

SQL Server数据库死锁处理超详细攻略

《SQLServer数据库死锁处理超详细攻略》SQLServer作为主流数据库管理系统,在高并发场景下可能面临死锁问题,影响系统性能和稳定性,这篇文章主要给大家介绍了关于SQLServer数据库死... 目录一、引言二、查询 Sqlserver 中造成死锁的 SPID三、用内置函数查询执行信息1. sp_w

Python UV安装、升级、卸载详细步骤记录

《PythonUV安装、升级、卸载详细步骤记录》:本文主要介绍PythonUV安装、升级、卸载的详细步骤,uv是Astral推出的下一代Python包与项目管理器,主打单一可执行文件、极致性能... 目录安装检查升级设置自动补全卸载UV 命令总结 官方文档详见:https://docs.astral.sh/

Before和BeforeClass的区别及说明

《Before和BeforeClass的区别及说明》:本文主要介绍Before和BeforeClass的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Before和BeforeClass的区别一个简单的例子当运行这个测试类时总结Before和Befor

Python pip下载包及所有依赖到指定文件夹的步骤说明

《Pythonpip下载包及所有依赖到指定文件夹的步骤说明》为了方便开发和部署,我们常常需要将Python项目所依赖的第三方包导出到本地文件夹中,:本文主要介绍Pythonpip下载包及所有依... 目录步骤说明命令格式示例参数说明离线安装方法注意事项总结要使用pip下载包及其所有依赖到指定文件夹,请按照以

Python包管理工具核心指令uvx举例详细解析

《Python包管理工具核心指令uvx举例详细解析》:本文主要介绍Python包管理工具核心指令uvx的相关资料,uvx是uv工具链中用于临时运行Python命令行工具的高效执行器,依托Rust实... 目录一、uvx 的定位与核心功能二、uvx 的典型应用场景三、uvx 与传统工具对比四、uvx 的技术实

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal

SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程

《SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程》LiteFlow是一款专注于逻辑驱动流程编排的轻量级框架,它以组件化方式快速构建和执行业务流程,有效解耦复杂业务逻辑,下面给大... 目录一、基础概念1.1 组件(Component)1.2 规则(Rule)1.3 上下文(Conte

Springboot3+将ID转为JSON字符串的详细配置方案

《Springboot3+将ID转为JSON字符串的详细配置方案》:本文主要介绍纯后端实现Long/BigIntegerID转为JSON字符串的详细配置方案,s基于SpringBoot3+和Spr... 目录1. 添加依赖2. 全局 Jackson 配置3. 精准控制(可选)4. OpenAPI (Spri

MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)

《MySQL复杂SQL之多表联查/子查询详细介绍(最新整理)》掌握多表联查(INNERJOIN,LEFTJOIN,RIGHTJOIN,FULLJOIN)和子查询(标量、列、行、表子查询、相关/非相关、... 目录第一部分:多表联查 (JOIN Operations)1. 连接的类型 (JOIN Types)