红 - 黑树是一种平衡二叉树被广泛用于,但对于很多人谁是新算法,红黑树实在是太复杂,看之前July博文,觉得自己的写作非常具体的。不过还是有点乱。不能急。要每天看一点点,所以我把插入和删除分开来写,仅仅要看懂并记住插入后是如何操作的,那么删除也就easy了。

红黑树的规则:

性质1. 节点是红色或黑色。

性质2. 根是黑色。

性质3. 全部叶子都是黑色(叶子是NIL节点)。

性质4. 每一个红色节点的两个子节点都是黑色。(从每一个叶子到根的全部路径上不能有两个连续的红色节点)

性质5. 从任一节点到其每一个叶子的全部简单路径都包括同样数目的黑色节点。

最基本的两条是4和5。一旦插入一个新的节点,那么4和5的性质就有可能破坏。从5中我们能够推出新增节点必须设为红色,而再依据4。新增节点的父节点必须是黑色。

我们推出的这最后一条很重要。由于一旦发现插入节点要插在一个红色节点下,就要開始折腾了!

红黑树的最基本操作是旋转(左旋和右旋)。因为对称性,将一个即可。

下图介绍右旋的一个样例(本文图皆来自《STL源代码剖析》)

我们从图中发现,右旋事实上就是改变了k1,k2的父子关系,那么其它节点怎么变呢?

能够这样理解。父亲要和儿子换位置,父亲好悲剧,那父亲的还有一个儿子和他的父子关系不能变(再变就太慘了),儿子长辈分了,那么儿子就把他的儿子交给原来的父亲当儿子吧,而操作是右旋,那就移动儿子的右孩子。

简记:右旋:孩子的右孩子给父亲当左孩子。

左旋:孩子的左孩子给父亲当右孩子。

默念三遍。就会发现旋转的思路就是这么简单!

代码例如以下(STL源代码。大师作品):

inline void _rb_rotate_right(_rb_tree_node_base* x,_rb_tree_node_base*& root)\\x为k2。root为根节点_rb_tree_node_base* y=x->left;   \\y为左孩子。也就是k1x->left=y->right;  \\孩子的左孩子成为父亲的右孩子 B接k2if(y->right!=0)y->right->parent=x;  \\  回马枪设定父节点y->parent=x->parent; \\k1開始跑到k2的位置if(x==root)root=y;else if(x==x->parent->right)   x->parent->right=y;   \\y代替x的地位,但要和x的原父亲的关系还原elsex->parent->left=y;y->right=x;       x->parent=y;  \\最后,父亲变成孩子,悲剧。
}

代码思路:1先把孩子的左孩子接到这个节点的右孩子上(2句)  2这个右孩子的父亲是这个节点(1句) 3、节点的左孩子代替节点的位置(1句)  4、和原节点父亲节点的关系保留(3种推断)  5、节点成为孩子节点的孩子(2句)

以下进入正题:插入节点

插入节点可能会带来3种不同情况的破坏,下图给出的是4种情况:(4仅仅是3的更复杂一点的情况)

我们细致观察这三种不同情况,同样的是插入节点的父亲都是红的。不同点有2:1是父亲的兄弟节点(叔节点)是什么颜色的?2新增节点是作为左孩子还是右孩子?

第一种情况:

叔黑+左孩子(黑左bl)

解决方法:右旋。

例如以下图:

说是右旋,旋哪个呢?新增节点肯定不动,以新节点的父亲P,爷爷为轴右旋G。旋转后这两个节点颜色都变(新增点肯定是红的)。

简记:黑左(bl)=右旋PG

助记:bl=r+pg(Boys’ Love RPG)

另外一种情况:

叔黑+右孩子(br)

解决方法:左旋+右旋

例如以下图:

我们从图中能够看出,先左旋X和P,再右旋X和G(变化X和G)。细致想想,第一次左旋似乎就是构造出第一种情况(图2先把8和10的颜色变化了,应是8红,10黑,那就和第一种情况一样)。

简记:黑右(br)=左旋XP。到达叔左。

助记:嘿哟,郎咸平(lXP)

第三种情况

叔红

这样的情况很easy。我们肯定叔节点不能有孩子了,有的话必须是黑色的,那叔节点那条分支的黑色就多了,所以我们能够随便改变叔节点的颜色(这是前提)。

例如以下图(来自July)

4是新增节点,4和5都红了。那就把5变黑,5黑后为了平衡的把8(叔节点)也变黑,都黑了后7这条分支比1分支多了一个黑色节点。所以7也要红,这样和2又都是红色的了,还得往上递归。这时候7相当玉新插入节点。得三种情况推断(图中这个符合另外一种情况),一直递归到根节点。

简记:变变变(PGU都变了),再推断。

最后来看SLT里面大师是如何写源码的。

首先,他的思路有所变换,他感觉第三种情况一直往上递归太慢,所以他採用了先处理的方法,例如以下图:

大致思路是:沿着插入节点往上看。假设有一个节点的两个子节点都是红色。那就把这个节点改成红色,他的子节点都改成黑色。假设此时这个节点的父节点也是红色(此时父节点的兄弟节点一定是黑的,请自己分析)就要像第一种或者第一种情况处理。

最后,插入节点的推断就仅仅剩第1和第2两种情况。

代码例如以下:

// 这是一个全局函数
// 又一次令树形平衡(改变颜色及旋转树形)
inline void _rb_tree_rebalance(_rb_tree_node_base* x , _rb_tree_node_base*& root)   //x新增节点,root为根节点
{  x->color = _rb_tree_red;    //新节点必为红  while(x != root && x->parent->color == _rb_tree_red)    // 假设父节点为红  {  if(x->parent == x->parent->parent->left)      // 父节点为祖父节点之左子节点  {  _rb_tree_node_base* y = x->parent->parent->right;    // 令y为伯父节点  if(y && y->color == _rb_tree_red)    // 伯父节点存在。且为红(第三种情况)  {  x->parent->color = _rb_tree_black;           // 更改父节点为黑色  y->color = _rb_tree_black;                   // 更改伯父节点为黑色  x->parent->parent->color = _rb_tree_red;     // 更改祖父节点为红色  x = x->parent->parent;  //祖父节点变成新增结点了}  else    // 无伯父节点,或伯父节点为黑色  {  if(x == x->parent->right)   // 假设新节点为父节点之右子节点 (另外一种情况) {  x = x->parent;  _rb_tree_rotate_left(x , root);    // 第一个參数为左旋点  }  x->parent->color = _rb_tree_black;     // 先改变颜色了  x->parent->parent->color = _rb_tree_red;  _rb_tree_rotate_right(x->parent->parent , root);    // (第一种情况)}  }  else          // 父节点为祖父节点之右子节点(和上面对立)  {  _rb_tree_node_base* y = x->parent->parent->left;    // 令y为伯父节点  if(y && y->color == _rb_tree_red)    // 有伯父节点,且为红
(第三种情况) {  x->parent->color = _rb_tree_black;           // 更改父节点为黑色  y->color = _rb_tree_black;                   // 更改伯父节点为黑色  x->parent->parent->color = _rb_tree_red;     // 更改祖父节点为红色  x = x->parent->parent;          // 准备继续往上层检查  }  else    // 无伯父节点,或伯父节点为黑色  {  if(x == x->parent->left)        // 假设新节点为父节点之左子节点 (另外一种情况) {  x = x->parent;  _rb_tree_rotate_right(x , root);    // 第一个參数为右旋点 (第一种情况)}  x->parent->color = _rb_tree_black;     // 先改变颜色  x->parent->parent->color = _rb_tree_red;  _rb_tree_rotate_left(x->parent->parent , root);    // 第一个參数为左旋点  }  }  }//while  root->color = _rb_tree_black;    // 根节点永远为黑色
}  

事实上,红黑树确实挺复杂的,变化太多,但仅仅要略微注意下几种情况的相互关系,比方3->2->1,理解就简单多了。

下一步学习删除节点。

版权声明:本文博客原创文章,博客,未经同意,不得转载。

连贯的学习黑树(插入节点)相关推荐

  1. Java之HashMap经典算法-红黑树(插入节点平衡调整,左旋转,右旋转)

    1. 红黑树的优势 有了二叉搜索树,为什么还需要平衡二叉树? 二叉搜索树容易退化成一条链,这时,查找的时间复杂度从 O(log2N)O(log_2N)O(log2​N) 将退化成 O(N)O(N )O ...

  2. HashMap-红黑树插入平衡、左旋、右旋源码解析

    目录 一.树的演变 二.红黑树 1.红黑树的特点 2.树左旋右旋的过程 3.红黑树插入节点情景分析: 三.HashMap插入平衡.左旋.右旋源码解析 1.添加值 2.插入平衡 3.左旋.右旋 一.树的 ...

  3. 底层实现红黑树_stl map底层之红黑树插入步骤详解与代码实现 | 学步园

    本篇文章并没有详细的讲解红黑树各方面的知识,只是以图形的方式对红黑树插入节点需要进行调整的过程进行的解释. 最近在看stl源码剖析,看到map底层红黑树的实现.为了加深对于红黑树的理解就自己动手写了红 ...

  4. 红黑树的创建、插入节点

    一.红黑树的特性 (1) 每个节点或者是黑色,或者是红色 (2) 根节点是黑色 (3) 每个叶子节点是黑色(并且是空的结点) (4) 不能出现两个连续的红色结点(如果一个节点是红色的,那么它的两个子节 ...

  5. 红黑树:节点插入详解及其红黑树自我实现

    红黑树:节点插入详解及其红黑树自我实现 红黑树的四个性质: 每个结点不是红色就是黑色 根节点是黑色的 如果一个节点是红色的,则它的两个孩子结点是黑色的 对于每个结点,从该结点到其所有后代叶结点的简单路 ...

  6. 红黑树----红黑树插入和删除结点的全程演示

    引言: 目前国内图书市场上,抑或网上讲解红黑树的资料层次不齐,混乱不清,没有一个完整而统一的阐述.而本人的红黑树系列四篇文章(详见文末的参考文献),虽然从头至尾,讲的有根有据,层次清晰,然距离读者真正 ...

  7. 【算法】红黑树插入数据(变色,左旋、右旋)(二)

    本人菜鸡一只,正在更新红黑树系列的文章. 该系列已经全部更完,有5篇文章: [算法]红黑树(二叉树)概念与查询(一):https://blog.csdn.net/lsr40/article/detai ...

  8. Algorithm:树相关算法(BBT/BST/B树/R树)简介(二叉查找树、二叉查找树的插入节点、二叉查找树的删除、二叉树的遍历、平衡二叉树)C 语言实现

    Algorithm:树相关算法(BBT/BST/B树/R树)简介(二叉查找树.二叉查找树的插入节点.二叉查找树的删除.二叉树的遍历.平衡二叉树)C++语言实现 目录 树的基础知识 1.二叉树的遍-前序 ...

  9. 红黑树的原理_红黑树插入算法实现原理分析

    ­ 引言 红黑树是在实际工程中被广泛应用的一种数据结构,比如Linux中的线程调度就是使用的红黑树来管理进程控制块,而Nginx中也是使用红黑树来管理的timer,Java中的TreeMap和Tree ...

最新文章

  1. 小酌重构系列[8]——提取接口
  2. 5位华人博士入选2021苹果学者!其中1名北大在读博士师从崔斌教授
  3. vb串口 任意波特率_C#与单片机串口通讯,实现简单的计数功能
  4. 一键安装lamp脚本--初级版
  5. 利用Topshelf把.NET Core Generic Host管理的应用程序部署为Windows服务
  6. 验证授权【msdn】
  7. Python的解析式与生成器
  8. 接口测试如何在json中引用mock变量
  9. 学习编写测试桩之declspec (dllexport)篇
  10. 自动驾驶_高精地图模型构成
  11. 将RGB转换为JPG格式到内存的代码
  12. UNIX环境高级编程 学习笔记 第十八章 终端I/O
  13. JS数值类型与字符串类型的内置方法
  14. 将一个数组划分成总和相等的两部分
  15. android 多开app store,原来 iOS 也有双开助手,还是上架了苹果官方 App Store 的
  16. 移动无线网卡服务器,贵阳移动无线网卡月租
  17. 大数据可视化-绘制景点热力图
  18. MIT公开课: Python 笔记6 二分法,牛顿-拉夫森方法,列表
  19. 给你的SpringBoot工程打的jar包瘦瘦身
  20. 传统基本图像处理方法:图像增强(灰度变换、直方图增强、空间域滤波、频率域滤波)、图像分割、图像配准等

热门文章

  1. DCMTK:将DICOM结构化报告文件的内容转换为XML格式
  2. VTK:可视化之FrogBrain
  3. VTK:Utilities之Vector
  4. VTK:几何对象之OpenVROrientedCylinder
  5. OpenCV LATCH Matching描述符匹配算法的实例(附完整代码)
  6. C++实现successive approximation渐进法(附完整源码)
  7. C++检查数字是否为2的幂的实现算法(附完整源码)
  8. C语言对内存地址的封装
  9. C++继承体系下的对象构造
  10. crash工具解析_IDA反汇编静态调试Android平台C++的so文件Crash入门