COSMIC的后端学习之路——1.1 随处可见的红黑树

本文深入探讨了红黑树的基础知识,包括其性质、左旋和右旋操作,以及在插入和删除节点时的平衡调整策略。红黑树常用于map实现、nginx、定时器等场景。文章提供了详细的代码实现,并指出红黑树在内存管理和进程调度中的应用。同时,介绍了判断叶子节点的方法以及删除节点的处理流程。
摘要由CSDN通过智能技术生成


本博客总结自零声教育的课程

本博客将和大家一起学习红黑树的相关知识,包括红黑树的性质、红黑树的左旋和右旋、红黑树增加、删除结点的原理,并在附有代码实现和讨论

1、知识树

在这里插入图片描述

2、红黑树的性质(3)

① 每个节点是红的或者黑的,根节点、叶子结点是黑色的
② 如果结点是红的,则两个儿子都是黑的(意思是红黑树中不能出现两个红色的结点相邻)
③ 对每个结点,从该节点到其子孙结点的所有路径上包含相同数目的黑结点(该条性质用来控制红黑树的平衡性,不是平衡所有结点,只是平衡的黑色结点的高度,平衡黑高)
在这里插入图片描述
(默认叶子结点都是黑色的)
在这里插入图片描述
(默认叶子结点都是黑色的,上图中没有画出叶子结点)

3、红黑树的使用(2)

★红黑树使用分两种情况:
①key,value:通过key查找对应的value
②利用红黑树的中序遍历,红黑树的中序遍历是顺序的

4、红黑树用在哪里(举例)

(1)map是红黑树的封装
(2)ngix
(3)定时器
(4)进程调度的cfs:用红黑树存储进程的集合,把调度的时间作为key值,每次从时间最小的值开始调度,即从红黑树的左下角开始查找
(5)内存管理:红黑树的平衡性使得我们能够更好地进行查找;表述一块内存有两种方法:①起始位置 + 长度 ②起始位置 + 结束位置,内存管理用红黑树进行存储的话,key的表示就是这两种方式

5、判断是否为叶子节点的方法(2)

① 判断左右子树是否同时为空
定义【一个公共的叶子结点】,看结点是否为这个公共的叶子结点,就能判断是否到达了叶子结点

6、红黑树的旋转

红黑树的旋转分为左旋和右旋:
在这里插入图片描述
左旋、右旋是我们实现红黑树的过程中的一个基础的原语操作,不要关注颜色

(1)左旋

左旋(逆时针旋转):x原本是y的父节点,现在将y变为x的父节点
★三步,每步对应修改一个树枝的两个方向,修改6个指针(因为每一个树枝对应两个方向的指针):
①将x的右子树指向y的左子树;若y的左子树不为叶子结点,则将y的左子树的父节点改为x的右子树
在这里插入图片描述

②将y的parent指向x的parent(判断x是否为根节点,即x的parent是否为叶子结点);原本x的parent的左子树或右子树指针指向y(先要判断x为x的parent的左子树还是右子树)

在这里插入图片描述

③将x的parent的指针指向y;将y的左子树的指针指向x
在这里插入图片描述

(2)右旋

右旋:复制上述代码,①将左右互换,②将x和y互换 就实现了,逻辑不需要修改
1、将y的左子树指向x的右子树,若x的右子树不为空则将x的右子树指向y
在这里插入图片描述

2、将x的parent指向y的parent,将y的parent指向x
在这里插入图片描述

3、将x的右子树指向y,y的parent指向x

7、操作红黑树结点(增删)的分情况讨论、原理和代码实现

(1)添加一个红黑树结点

如何插入一个结点:★新增加的一个结点,一定是加到非底层的叶子结点处
举例:若加一个结点,key为500,则加到图中红笔处:
在这里插入图片描述
在红黑树中插入一个结点,分为以下几种情况:
在这里插入图片描述
★★★在分情况讨论的时候一定要牢记两个点:
① 插入当前结点之前就是一棵红黑树
② 只讨论所插入的结点z是红色的

解读上图:
1、插入z结点,若z为黑色,则比较麻烦,建议直接看代码
2、插入z结点,若z为红色,则要z要插入的位置的父节点的颜色
(1)若父结点是黑色,则不需要调整,直接插入即可
(2)父节点是红色,祖父结点只能是黑色,此时要再按照叔父结点的颜色分为两类(知道叔父结点的颜色是为了方便做旋转):
① 若叔父结点是红色,则只有一种情况
② 若叔父结点是黑色,则还需要讨论两种情况:
a. 插入结点是父节点的左子树
b. 插入结点是父节点的右子树
注意:【若叔父结点是黑色的且下面还有叶子结点】这种情况不成立,因为父节点是红色的,所以若叔父结点是黑色的则只能是叶子结点

a. 若插入结点是父节点的右子树:先左旋,再修改颜色,再右旋
举例:
在这里插入图片描述
1)左旋344,将350放到344的位置上
在这里插入图片描述
2)右旋359,变成350、344、359(中左右)
在这里插入图片描述
b. 若插入结点是父节点的左子树:先修改颜色,再右旋
举例:(若添加340)=>
b. 叔父结点是红色的,此时不用看插入结点是父节点的左子树还是右子树,都是一样的:将父节点和叔父结点都变黑,将祖父结点变红即可,这样变换后插入结点的父节点就为黑色了,这时将红色的插入结点直接插入即可;变换颜色后,祖父结点388变成了红色,为了不让祖父结点和祖父的父节点颜色冲突,要将插入z结点变成祖父结点(将祖父结点赋值给z结点并递归调用),递归调用函数向上判断:(若添加370)

★红黑树左右子树高度差最多不超过2倍(2n-1)
举例:
若左子树高度是5,则右子树高度最多不超过9

(2)删除一个红黑树结点

有3个结点参与删除:
① 删除的key所对应的结点z
② 要删除的结点的后继结点y(注意要删除的结点不是key对应的结点,而是key对应的结点的后继节点)
③ 轴心结点x(后面需要调整的结点)
★★★过程:将y的值复制到z结点上,删除y结点,再调整x结点;z是要被覆盖的结点,y才是真正要被删除的结点

注意,若删除的y结点是黑色的,才需要调整;若y为红色则不需要调整

举例:这种情况下,删除key为172的结点(注意删除的不是key为172的结点,而是该节点的后继节点)
在这里插入图片描述
①删除的key所对应的结点z:key为172对应的结点
②所要删除的结点y(注意要删除的结点不是key对应的结点,而是key对应的结点的后继节点):key为206所对应的结点
③轴心结点x(后面需要调整的结点):key对224所对应的结点
★★★过程:将y(206对应的结点)的值复制到z结点上,删除y结点,再调整x结点

红黑树结点的删除的分情况讨论比较麻烦,建议直接看删除红黑树结点的代码;面试手撕红黑树结点的可能性不大,手撕红黑树代码主要能够帮助我们在以后的工作中修改底层源码。

8、代码实现

上述详细介绍了红黑树的相关原理,红黑树相关的代码均在我的github中,代码中每条语句都有详细的注释,以下是我的github代码链接,恳请大家批评指正!
GitHub代码
我自己实现的rbtree.c代码在rbtree文件夹中
课程中的参考代码在0voice文件夹的rbtree文件夹中

9、感谢大家的观看,我是COSMIC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值