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

相关文章

Go语言中make和new的区别及说明

《Go语言中make和new的区别及说明》:本文主要介绍Go语言中make和new的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1 概述2 new 函数2.1 功能2.2 语法2.3 初始化案例3 make 函数3.1 功能3.2 语法3.3 初始化

Python设置Cookie永不超时的详细指南

《Python设置Cookie永不超时的详细指南》Cookie是一种存储在用户浏览器中的小型数据片段,用于记录用户的登录状态、偏好设置等信息,下面小编就来和大家详细讲讲Python如何设置Cookie... 目录一、Cookie的作用与重要性二、Cookie过期的原因三、实现Cookie永不超时的方法(一)

java中新生代和老生代的关系说明

《java中新生代和老生代的关系说明》:本文主要介绍java中新生代和老生代的关系说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、内存区域划分新生代老年代二、对象生命周期与晋升流程三、新生代与老年代的协作机制1. 跨代引用处理2. 动态年龄判定3. 空间分

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

SpringBoot整合liteflow的详细过程

《SpringBoot整合liteflow的详细过程》:本文主要介绍SpringBoot整合liteflow的详细过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋...  liteflow 是什么? 能做什么?总之一句话:能帮你规范写代码逻辑 ,编排并解耦业务逻辑,代码

MySQL之InnoDB存储引擎中的索引用法及说明

《MySQL之InnoDB存储引擎中的索引用法及说明》:本文主要介绍MySQL之InnoDB存储引擎中的索引用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录1、背景2、准备3、正篇【1】存储用户记录的数据页【2】存储目录项记录的数据页【3】聚簇索引【4】二

mysql中的数据目录用法及说明

《mysql中的数据目录用法及说明》:本文主要介绍mysql中的数据目录用法及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、版本3、数据目录4、总结1、背景安装mysql之后,在安装目录下会有一个data目录,我们创建的数据库、创建的表、插入的

浏览器插件cursor实现自动注册、续杯的详细过程

《浏览器插件cursor实现自动注册、续杯的详细过程》Cursor简易注册助手脚本通过自动化邮箱填写和验证码获取流程,大大简化了Cursor的注册过程,它不仅提高了注册效率,还通过友好的用户界面和详细... 目录前言功能概述使用方法安装脚本使用流程邮箱输入页面验证码页面实战演示技术实现核心功能实现1. 随机

使用Python进行GRPC和Dubbo协议的高级测试

《使用Python进行GRPC和Dubbo协议的高级测试》GRPC(GoogleRemoteProcedureCall)是一种高性能、开源的远程过程调用(RPC)框架,Dubbo是一种高性能的分布式服... 目录01 GRPC测试安装gRPC编写.proto文件实现服务02 Dubbo测试1. 安装Dubb

HTML img标签和超链接标签详细介绍

《HTMLimg标签和超链接标签详细介绍》:本文主要介绍了HTML中img标签的使用,包括src属性(指定图片路径)、相对/绝对路径区别、alt替代文本、title提示、宽高控制及边框设置等,详细内容请阅读本文,希望能对你有所帮助... 目录img 标签src 属性alt 属性title 属性width/h