红黑树的概念

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

红黑树的性质

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

问题

为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两 倍?

极端情况下,刚好是两倍,但是这种极端情况不存在。因为根结点是黑的,那么第二层的两个结点都必须是红色的。

红黑树的结构

红黑树结点的定义

enum COLOR{RED,BLACK};template<class T>
struct RBTreeNode
{RBTreeNode(const T& data = T(), COLOR color = RED):_pLeft(nullptr), _pRight(nullptr), _pParent(nullptr), _data(data), _color(color)  {}RBTreeNode<T>* _pLeft;RBTreeNode<T>* _pRight;RBTreeNode<T>* _pParent;T _data;COLOR _color;
};
为什么要将结点的默认颜色给成红色的?

因为如果默认颜色是黑色的话,往已经是一颗红黑树里插入的话,性质4一定遭到破坏,所以给成红色,有些情况下破坏,有些情况下不被破坏

红黑树的插入

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

1. 按照二叉搜索的树规则插入新节点

template<class T>
class RBTree
{typedef RBTreeNode<T> Node;
public:RBTree(){_pHead = new Node;_pHead->_pLeft = _pHead;_pHead->_pRight = _pHead;//构造里已经将双亲给成空}bool Insert(const T&data){Node* pRoot = GetRoot();if (nullptr == pRoot) //树为空,创建根结点{pRoot = new Node(data,BLACK);pRoot->_pParent = _pHead;//只有一个结点,head就是根节点的双亲_pHead->_pLeft = pRoot; //改变左右指针域_pHead->_pRight = pRoot;return true;}else{//说明树已经不为空了//1.按照二叉搜索树的性质找到带插入结点在红黑树的位置Node* pCur = pRoot;Node* pParent = nullptr;while (pCur){pParent = pCur;//标记双亲位置if (data < pCur->_data)pCur = pCur->_pLeft;else if (data>pCur->_data)pCur = pCur->_pRight;else//相同不插入return false;}//2. 插入新结点pCur = new Node(data);if (data < pParent->_data)pParent->_pLeft = pCur;else{pParent->_pRight = pCur;}//3. 更新双亲位置pCur->_pParent = pParent;//4.检测:是否新结点插入后连在一起的红色结点if (RED == pParent->_color){//调整//检测新节点插入后,红黑树的性质是否造到破坏}}//5.调整头结点的左右指针域//保证根节点是黑色pRoot->_color = BLACK;_pHead->_pLeft=leftMost();_pHead->_pRight = RightMost();return true;}Node* LeftMost(){//得到根节点Node* pRoot = GetRoot();if (nullptr == pRoot)return _pHead;Node* pCur = pRoot;//找到最左侧结点while (pCur->_pLeft)pCur = pCur->_pLeft;return pCur;}Node* RightMost(){//得到根节点Node* pRoot = GetRoot();if (nullptr == pRoot)return _pHead;Node* pCur = pRoot;//找到最右侧结点while (pCur->_pRight)pCur = pCur->_pRight;return pCur;}
protected:Node*& GetRoot()  //head是new出来的,head存在parent一定存在,按引用方式返回没有问题{//得到根节点,也就是头结点的下一个结点return _pHead->_pParent;}
private:Node* _pHead;
};

2. 检测新节点插入后,红黑树的性质是否造到破坏

因为新节点的默认颜色是红色,因此:如果其双亲节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的双亲节点颜色为红色时,就违反了性质三不能有连在一起的红色节点此时需要对红黑树分情况来讨论
cur为当前节点,p为父节点,g为祖父节点,u为叔叔节点

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

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

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

p为g的左孩子,cur为p的左孩子,则进行右单旋转;相反,
p为g的右孩子,cur为p的右孩子,则进行左单旋转
p、g变色–p变黑,g变红

  • 如果u不存在,假设cur本来就存在,cur和双亲比然是黑的,因为两个红的不能连在一起,那么这条路径里就有两个黑色,所以不满足性质4,cur所以一定是新插入的结点
  • 如果u存在且为黑色,右侧路径里有两个黑色路径,因为两条路径黑色结点必须一样。新节点一插入,插入到cur的子树中,导致子树中不满足情况。向上调整时,把cur改成黑色。

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

红黑树的验证

红黑树的检测分为两步:

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  2. 检测其是否满足红黑树的性质
template<class T>
class RBTree
{typedef RBTreeNode<T> Node;
public:RBTree(){_pHead = new Node;_pHead->_pLeft = _pHead;_pHead->_pRight = _pHead;//构造里已经将双亲给成空}bool Insert(const T&data){Node*& pRoot = GetRoot(); //必须以引用的方式进行接受if (nullptr == pRoot) //树为空,创建根结点{pRoot = new Node(data,BLACK);pRoot->_pParent = _pHead;//只有一个结点,head就是根节点的双亲_pHead->_pLeft = pRoot; //改变左右指针域_pHead->_pRight = pRoot;return true;}else{//说明树已经不为空了//1.按照二叉搜索树的性质找到带插入结点在红黑树的位置Node* pCur = pRoot;Node* pParent = nullptr;while (pCur){pParent = pCur;//标记双亲位置if (data < pCur->_data)pCur = pCur->_pLeft;else if (data > pCur->_data)pCur = pCur->_pRight;else//相同不插入return false;}//2. 插入新结点pCur = new Node(data);if (data < pParent->_data)pParent->_pLeft = pCur;elsepParent->_pRight = pCur;//3. 更新双亲位置pCur->_pParent = pParent;//以上没错//4.检测:是否新结点插入后连在一起的红色结点while (pParent!=_pHead && RED == pParent->_color){Node* granderFather = pParent->_pParent;if (pParent == granderFather->_pLeft){//叔叔结点在右侧Node* uncle = granderFather->_pRight;//情况一:叔叔结点存在,且为红if (uncle && RED == uncle->_color){pParent->_color = BLACK;uncle->_color = BLACK;granderFather->_color = RED;pCur = granderFather;pParent = pCur->_pParent;}//以上没问题else{//情况三if (pCur == pParent->_pRight) //情况三{//转变成情况二RotateLeft(pParent);swap(pParent, pCur);}//情况二pParent->_color = BLACK;granderFather->_color = RED;RotateRight(granderFather);}//以上没问题}else{//叔叔结点在左侧Node* uncle = granderFather->_pLeft;//情况一的反情况if (uncle && uncle->_color == RED){pParent->_color = BLACK;uncle->_color = BLACK;granderFather->_color = RED;pCur = granderFather;pParent = pCur->_pParent;}//以上没问题else{//情况三的反情况if (pCur == pParent->_pLeft)    /**/{//情况三的反情况变成情况二的反情况RotateRight(pParent);swap(pParent, pCur);}//情况二反情况处理pParent->_color = BLACK;granderFather->_color = RED;RotateLeft(granderFather);}//以上没问题}}}//5.调整头结点的左右指针域//保证根节点是黑色pRoot->_color = BLACK;_pHead->_pLeft = LeftMost();_pHead->_pRight = RightMost();return true;}void InOrder(){_InOrder(GetRoot());}//检测红黑树bool IsValidRBTree(){Node* pRoot = GetRoot();if (nullptr == pRoot)return true;if (pRoot->_color != BLACK){cout << "违反性质2:根结点颜色必须为黑色" << endl;return false;}//获取一条路径中结点的个数size_t blackCount = 0; //基准值Node* pCur = pRoot;while (pCur){if (pCur->_color == BLACK)blackCount++;pCur = pCur->_pLeft;}size_t pathBlack = 0; //每条路径中的黑色结点个数return _IsValidRBTree(pRoot, blackCount,pathBlack);}
protected:bool _IsValidRBTree(Node* pRoot, size_t blackCount, size_t pathBlack){if (nullptr == pRoot)return true;if (pRoot->_color == BLACK)pathBlack++;//检测性质3Node* pParent = pRoot->_pParent;if (pParent != _pHead && pParent->_color == RED&&pRoot->_color == RED){cout << "违反性质3:不能有连在一起的红色结点" << endl;return false;}if (nullptr == pRoot->_pLeft&&nullptr == pRoot->_pRight){//一条路径到叶子if (blackCount != pathBlack){cout << "违反了性质4:每条路径中黑色结点个数必须相同" << endl;return false;}}return _IsValidRBTree(pRoot->_pLeft, blackCount, pathBlack) &&_IsValidRBTree(pRoot->_pRight, blackCount, pathBlack);}Node* LeftMost(){//得到根节点Node* pRoot = GetRoot();if (nullptr == pRoot)return _pHead;Node* pCur = pRoot;//找到最左侧结点while (pCur->_pLeft)pCur = pCur->_pLeft;return pCur;}Node* RightMost(){//得到根节点Node* pRoot = GetRoot();if (nullptr == pRoot)return _pHead;Node* pCur = pRoot;//找到最右侧结点while (pCur->_pRight)pCur = pCur->_pRight;return pCur;}void RotateLeft(Node* pParent){Node* pSubR = pParent->_pRight;Node* pSubRL = pSubR->_pLeft;pParent->_pRight = pSubRL;if (pSubRL)pSubRL->_pParent = pParent;pSubR->_pLeft = pParent;Node* pPParent = pParent->_pParent;pParent->_pParent = pSubR;pSubR->_pParent = pPParent;if (pPParent == _pHead)GetRoot() = pSubR;else{if (pPParent->_pLeft == pParent)pPParent->_pLeft = pSubR;elsepPParent->_pRight = pSubR;}}void RotateRight(Node* pParent){Node* pSubL = pParent->_pLeft;Node* pSubLR = pSubL->_pRight;pParent->_pLeft = pSubLR;if (pSubLR)pSubLR->_pParent = pParent;pSubL->_pRight = pParent;Node* pPParent = pParent->_pParent;pParent->_pParent = pSubL;pSubL->_pParent = pPParent;//pParent是根结点if (pPParent == _pHead)GetRoot() = pSubL;else{//非根结点if (pPParent->_pLeft == pParent)pPParent->_pLeft = pSubL;elsepPParent->_pRight = pSubL;}}Node*& GetRoot()  //head是new出来的,head存在parent一定存在,按引用方式返回没有问题{//得到根节点,也就是头结点的下一个结点return _pHead->_pParent;}void _InOrder(Node* pRoot){if (pRoot){_InOrder(pRoot->_pLeft);cout << pRoot->_data << " ";_InOrder(pRoot->_pRight);}}
private:Node* _pHead;
};void TestRBTree()
{int array[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };RBTree<int>t;for (auto e : array)t.Insert(e);t.InOrder();if (t.IsValidRBTree()){cout << "t is vaild rbTree" << endl;}elsecout << "t is not vaild rbTree" << endl;
}

红黑树的删除

参考链接1
参考链接2

红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。

红黑树的应用

  1. Java中的java.util.TreeMap,java.util.TreeSet
  2. C++ STL中的:map,multimap,multiset
  3. .NET中的:SortedDictionary,SortedSet 等

模拟实现map和set

模拟实现

红黑树概念及其相关操作的实现相关推荐

  1. 2021-10-15 红黑树 概念和平衡操作理解以及与AVL对比分析 恋上数据结构笔记

    文章目录 红黑树的由来 红黑树需要遵守的五大规则 红黑树与4阶B树的相互转换!! 红黑树的插入(12种情况) 红黑树的删除(3大类情况) 红黑树的平衡 以及与AVL树的性能比较 红黑树的由来 红黑树: ...

  2. OpenShift 4 之 Image Registry、Image 和 ImageStream 概念和相关操作

    <OpenShift 4.x HOL教程汇总> OpenShift 4 之 Image Registry.Image 和 ImageStream 概念和相关操作 概念篇 1. Contai ...

  3. 红黑树详解(一)红黑树的介绍和操作

    红黑树详解(一)红黑树的介绍和操作 摘要: 在很多源码涉及到大量数据处理的时候,通常都是用红黑树这一数据结构.红黑树是一种自平衡的二叉查找树,它能在进行插入和删除操作时通过特定操作保持二叉查找树的平衡 ...

  4. 红黑树 键值_Java集合框架:红黑树概念、插入及旋转操作详细解读就问你会不会...

    初识TreeMap 之前的文章讲解了两种Map,分别是HashMap与LinkedHashMap,它们保证了以O(1)的时间复杂度进行增.删.改.查,从存储角度考虑,这两种数据结构是非常优秀的.另外, ...

  5. 终于搞懂红黑树!--红黑树的原理及操作

    红-黑-树 介绍: 红黑树( Red black tree)是种自平衡二叉查找树,在计算机科学中用到的一种数据结构. 它是在1972年由 Rudolf Bayer发明的当时被称为平衡二叉B树( sym ...

  6. python【数据结构与算法】红黑树概念辨析

    文章目录 1 二叉查找树 2 AVL 3 红黑树 1 二叉查找树 二叉查找树,Binary Search Tree 「BST」,要想了解二叉查找树,我们首先看下二叉查找树有哪些特性呢? 某节点的左子树 ...

  7. 数据库视图的概念和相关操作合集

    目录 视图的概念 创建视图 删除视图 查看视图的格式 视图的修改 视图的更新 视图和表的对比 综合大实验 视图的概念 视图: MySQL从5.0.1版本开始提供视图功能.一种虚拟存在的表,行和列的 数 ...

  8. Postgresql关于wal日志总结,一文搞清楚它的所有概念和相关操作

    官方文档 https://www.postgresql.org/docs/11/wal-intro.html https://www.postgresql.org/docs/11/wal-config ...

  9. 文件权限概念,相关操作

    一,文件权限的基本概念 权限:操作系统限制对资源访问的一种机制. 文件权限的信息展示,使用ls -l 命令即可查看: 整个文件信息可以分为以下几部分: (一)第一个字段表示文件类型 和 文件权限. 第 ...

最新文章

  1. 使用flask-WTF,Flask-Login,sqlite3实现登录和注册(前端登录和注册页面)
  2. spring29: JdbcTemplate详解
  3. 第18届浙江大学校赛 Mergeable Stack
  4. GetProcAddress() LoadLibrary() DLL
  5. 计算机辅助工程分析及应用论文,计算机辅助工程计量的论文
  6. 学习MiniGui之多线程机制【转】
  7. VMware 报错:“另一个程序已锁定文件的一部分,进程无法访问”---Linux运维工作笔记051
  8. 12.15daily_scrum
  9. Java基础知识陷阱(九)
  10. 分布式光纤振动传感技术在电力电缆管道防外力破坏的应用
  11. tinder和bumble_发布课程:Tinder,Reddit,Airbnb,Etsy和Uber如何吸引了第一批用户
  12. 【UVM基础】+uvm_set_verbosity 使用介绍
  13. hd620显卡驱动 linux,倍控工控机i7 7500U PVE下核显HD620 HDMI直通成功
  14. 服务器系统访问量统计,通过网站统计或系统监视器查看IIS并发连接数
  15. 【P2P网络】磁力链接转换为种子文件 magnet to torrent .
  16. 原笔迹手写实现平滑和笔锋效果之:笔锋效果(三)[完结篇]
  17. 迅雷方舟与花瓣:不一样的“瀑布流”
  18. python中如何保存并使用训练好的模型并调用
  19. 运放构成的电压跟随器
  20. 缓存服务器 之 Linux下缓存服务器的应用

热门文章

  1. 命令行获取docker远程仓库镜像列表
  2. Codeforces 765F. Souvenirs
  3. bzoj1049[HAOI2006]数字序列
  4. PhiloGL学习(5)——神说要有光,便有了光
  5. 004-JQuery属性
  6. 极光推送JPush的快速集成
  7. Web工程师必备的43款可视化工具
  8. 设备场景函数——72个
  9. 用计算机画有常数的函数图像,信息技术应用 用计算机画函数图象教学设计及教案分析...
  10. php mysql无限分类排序_PHP 无限级分类、排序