【数据结构】红黑树

📝前言:

这篇文章我们来讲讲红黑树,本文章在有AVL树的基础上进行讲解。

🎬个人简介:努力学习ing

📋个人专栏:C++学习笔记

🎀CSDN主页 愚润求学

🌄其他专栏:C语言入门基础python入门基础python刷题专栏Linux


文章目录

一,复习AVL的旋转操作

AVL树要求,左右子树高度差不超过1,当超过 1 的时候就要进行旋转操作。

旋转操作又分为单旋和双旋,当父亲和孩子同侧高时用单旋,异侧高时用双旋。

下面以右单选和左右双旋为例。(左右双旋其实也是两次单旋,孩子先左单选,变成同侧高,然后再对父亲右单旋)

当父亲A和孩子B同为左边高时(如图2),需要对A进行右单旋

右单旋关键步骤:

  1. 把B的右孩子BR给 A 的左边
  2. 然后把A压下来,当做B的右孩子

当下面这种情况就要进行双旋:

我们把插入后的BR展开,更好看两次单旋的过程拆分

先对孩子B进行左单旋,再对父亲A进行右单旋

这里的平衡因子结果只是在一开始插入后,C的平衡因子为零的结果。其他插入情况需要具体情况具体分析,如果不嫌弃可以看我的笔记:

二,红黑树的特性

红黑树是⼀棵⼆叉搜索树,他的每个结点增加⼀个存储位来表示结点的颜色,可以是红色或者黑色。通过对任何⼀条从根到叶子的路径上各个结点的颜色进行约束,红黑树确保没有⼀条路径会比其他路径长出2倍,因而是接近平衡的

1 主要特性

主要的四大特性:

1. 每个节点不是红色就是黑色
2. 根节点是黑色
3. 红色节点的两个孩子必须是黑色
4. 对于任意一个节点,从该节点到其所有NULL结点的简单路径上,均包含相同数量的黑色结点(注意看路径的时候要数到空节点)

有些书上把这些空节点叫做NIL并且认为它们是黑色的

如:

通过规则2,3,4我们可以很容易求证:红黑树确保没有⼀条路径会比其他路径长出2倍。因为,最短路径情况:全是黑。最长路径情况:一黑一红。

2 算法效率

因为最长路径不会大于最短路径的两倍,而最短路径的最后一个节点所在层往上是一颗完全二叉树(因为要满足路径上有相同个数的黑色节点),所以,就算最长路径为最短路径的两倍,高度也不过:2*log(N)(N为节点个数),查找时间复杂度还是O(log(N))

(可见上图,节点10以上是一颗满二叉树,最长路径到达的节点50,深度只是节点10对应的满二叉树的两倍)

虽然查找的效率上可能比不过AVL树,但是因为红黑树对"平衡"的要求更宽松,所以,在调整操作的次数上,比AVL树少的多。

三,红黑树的调整方法

红黑树和AVL树大致相同,不过红黑树不利用平衡因子来标识是否平衡,而是通过节点的颜色来进行判断。

什么是调整

所谓调整平衡就是:每插入一个节点时,都通过调整,重新让红黑树满足前面提到的4大特性,使其依然是红黑树。

首先,我们思考一个问题:新插入的节点应该是什么颜色?

答案:必须是红色。

因为,插入黑色必然会导致当前路径上的黑色节点数量和其他路径不同了。(插入都是找找到NULL位置插入的,别忘了)

那插入时,什么时候需要调整呢?

答案:当插入的节点的父亲节点是红色时才需要调整!(此时出现了连红,父亲孩子都是红色

当父亲为黑色时,插入后,依然满足4大特性,不需要调整

调整又分为下面4不同的场景:

在具体了解之前,我们先来理解一下下面这幅图:

如果我们把father节点的父亲叫做grandfathercur代表我们插入的节点。

则,当插入红色节点后,三个节点的颜色是一定的:cur:红色,father:红色,grandfather:黑色(因为father原来是红色,grandfather原来只能是黑色)

这时候只有uncle不确定(uncle是唯一变量,也是我们采取不同调整策略的决定因素

场景一:U为红

场景:

(当然F也可以是G的右孩子,C也可以是F的右孩子,这里只是示例)

你也可以暂停,先自己想一想怎么调整。

方法:仅变色

  1. F变黑(无论什么场景下F都要变黑,因为F不能和C连红
  2. G变红
  3. U变黑

具体流程图:

我们可以想想为什么这样变?

  1. F必须要变黑,解决了连红 ,但是导致左侧这一路多了1个黑
  2. G变红,所有经过G的路径少一个黑,解决了1带来的问题,但是导致U这一路也少了一个黑
  3. U变黑,补回来一个黑

G变红了,但是,G的父节点(图中空白的节点)也是红呢?那就要把G当C继续向上调整。

【也就是说,其实C可以不仅代表一个节点,也可以代表一颗树(这可太关键了) ,因为如果代表一颗树,C原来是grandfather,为黑色。从grandfather这个节点往下,肯定也是一颗红黑树。】

你要问我为什么?因为满足四大特性,回头去看吧!

场景二:U不存在

此时C一定是新增的。

因为如果不是新增的,代表原来为下面子树的根节点grandfather变上来的,原来是黑色,但是这显然不可能,因为违反了特性,如图:

路径1比路径2少一个黑节点

场景:

方法(变色+单旋):

  1. F变黑
  2. G变红
  3. 单旋转

为什么要单旋?

F变黑,多一个黑。G变红,减了一个黑,原来从G节点右孩子NULL出的路径也少了一个黑(所以,可见,当一个黑节点作为路径终点的时候,无法轻易改变颜色)

具体流程

因为此时,F为黑,这时候无须再往上调整。

场景三:U为黑色,GFC成直线

这个场景下,C一定是原来的grandfather变来的。(不过多解释了,画图易知)

方法(变色+单旋):

  1. F变黑
  2. G变红
  3. 单旋

思路和场景二一样,为什么单旋,原因也一样,就不解释了。

这样操作以后也无须再向上更新,F的右孩子也不用担心和G连红,右孩子的根节点肯定是黑,因为本来F就是红

场景四:GFC不成直线,U为黑色或不存在

同样,U不存在,则C⼀定是新增结点,U存在且为⿊,则C⼀定不是新增

方法(变色+双旋)【我更喜欢记作:单旋 + (变色 + 单旋)(场景三)】:

  1. C 变黑
  2. G 变红
  3. 双旋

为什么变的是C和G?

我们先做一次单旋:

这就是场景三(只不过C与F换了位置,这就是变C颜色的原因),记住双旋多做一次旋就是为了转换成单旋的场景

具体流程:

调整方法总结

  • U为红:仅变色(FGU)
  • U不存在:变色(FG) + 单旋
  • U为黑色,GFC成直线:变色(FG) + 单旋
  • GFC不成直线,U为黑色或不存在:变色(CG)+ 双旋【单旋 + 变色(CG) + 单旋】

🌈我的分享也就到此结束啦🌈

要是我的分享也能对你的学习起到帮助,那简直是太酷啦!

若有不足,还请大家多多指正,我们一起学习交流!

📢公主,王子:点赞👍→收藏⭐→关注🔍

感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

相关推荐
沐墨专攻技术1 小时前
《空间复杂度(C语言)》
c语言·数据结构·算法·空间复杂度
wuqingshun3141592 小时前
蓝桥杯 6. k倍区间
c++·算法·职场和发展·蓝桥杯·深度优先
豆豆3 小时前
day26 学习笔记
笔记·opencv·学习·计算机视觉
努力学习的小廉3 小时前
【C++】 —— 笔试刷题day_20
开发语言·c++
涛ing4 小时前
【Linux “less“ 命令详解】
linux·运维·c语言·c++·人工智能·vscode·bash
xxjiaz5 小时前
二分查找-LeetCode
java·数据结构·算法·leetcode
算法练习生5 小时前
数据结构学习笔记 :排序算法详解与C语言实现
数据结构·学习·排序算法
Hello eveybody6 小时前
C++按位与(&)、按位或(|)和按位异或(^)
开发语言·c++
被AI抢饭碗的人6 小时前
c++:c++中的输入输出(二)
开发语言·c++