红-黑树的概念
出于网上有很多讲解红黑树的文章,但我觉得都不完全正确,在查阅了众多文章后,决定自己总结一篇讲解红黑树增删的文章,如有疏漏错误之处,还请纠正。
红黑树必须要遵守的规则,称为红-黑规则:
- 每个节点不是红色就是黑色的;
- 根节点总是黑色的;
- 如果节点是红色的,则它的子节点必须是黑色的(反之不一定),也就是从每个叶子到根的所有路径上不能有两个连续的红色节点;
- 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。
注意: 新插入的节点颜色总是红色的,这是因为插入一个红色节点比插入一个黑色节点违背红-黑规则的可能性更小,原因是插入黑色节点总会改变黑色高度(违背规则4),但是插入红色节点只有一半的机会会违背规则3(因为父节点是黑色的没事,父节点是红色的就违背规则3)。另外就是违背规则3比违背规则4要更容易修正。
思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点个数的两倍?
最短路径为全黑,最长路径就是红黑节点交替(因为红色节点不能连续),每条路径的黑色节点相同,则最长路径、刚好是最短路径的两倍。
红黑树和AVL树比较:
都是高效的平衡二叉树,增删改查的时间复杂度都是O(log2N ),红黑树不追求绝对平衡,只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
红黑树的自我修正
红-黑树主要通过三种方式对平衡进行修正,改变节点颜色、左旋和右旋。
1. 插入操作
名称约定:
1.1 红黑树是空树
作为根节点直接插入,再设置为黑色
1.2 插入节点的父节点是黑色
直接插入,无需任何处理
1.3 插入节点的父节点是红色
推理:如果插入节点的父结点为红色,那么该父结点不可能为根结点,所以插入结点总是存在祖父结点。
1.3.1 插入节点的叔叔节点也是红色
如果插入节点的父节点P和叔叔节点S都是红色,那就将父节点和叔叔节点都变成黑色,将祖父节点PP变成红色(祖父节点原来必定是黑色的),如果此时祖父节点PP的父节点是黑色(图中未展示),那么无需任何处理插入操作结束,但如果祖父节点PP的父节点是红色,那就违反了上下相邻节点不能同是红色的规则,此时需要将PP节点当做新插入的节点再做自平衡操作,直到平衡为止。假如PP节点刚好是根节点,往上没有父节点了,那就必须把PP设置为黑色。
1.3.2 插入节点的叔叔节点是黑色或不存在
如果插入节点的父节点是红色,那么它的叔叔节点不可能是黑色,因为左右黑高不相等,违反了红黑树的规则,所以只能是不存在叔叔节点
这句话为什么删掉,是因为有可能还有一个黑色兄弟节点,如下
当我插入120时,90和110都要变黑,然后100变红,此时80也是红色,继续将100当成新插入的元素看待,此时就变成了父节点是红,叔叔节点50是黑色的情况了,该怎么做?看3.3.2.2
1.3.2.1 插入节点是父节点的左子节点,父节点是祖父节点的左子节点(父子都在左边)
这时还是先变色,将父节点P变成黑色,祖父节点PP变成红色,然后对PP进行右旋(因为父子节点都在左边,为了保持平衡,需要右旋)
1.3.2.2 插入节点是父节点的右子节点,父节点是祖父节点的右子节点(父子都在右边)
同理,先变色,将父节点P变成黑色,祖父节点PP变成红色,然后对PP进行左旋(因为父子节点都在右边,为了保持平衡,需要左旋)
1.3.2.3 插入节点是父节点的右子节点,父节点是祖父节点的左子节点(子在右,父在左)
当父子节点不在同一边时,需要先对父节点进行旋转,旋转后就在同一边了,就可以按照父子在同一边时的方式进行调整,同3.3.2.1。
针对本图的情况而言,父在左,子在右,将子左旋(想让节点往哪边就往哪边旋转)
1.3.2.4 插入节点是父节点的左子节点,父节点是祖父节点的右子节点(子在左,父在右)
针对本图的情况而言,父在右,子在左,将子右旋(想让节点往哪边就往哪边旋转),然后按照父子都在右边的情况3.3.2.2进行操作
练习插入操作
题目
答案
2.删除操作
还是和普通二叉树删除的情况类似,(如果我们不考虑节点的颜色)根据删除节点子节点的数量分三大种
- 没有子节点
叶子节点直接删除掉即可
- 两个子节点
将右子树中最小节点替换待删除节点,然后将最小节点删除——>转化成了删除右子树中最小节点
右子树中最小节点要么是叶子节点(直接删除即可);要么是根节点(只有一个右子节点)——>转化成了删除一个子节点的情况(情况3)
- 一个子节点
将父节点直接指向待删除节点的子节点
最后再去判断颜色变化
2.1 删除节点无子节点(删除叶子节点)
2.1.1 删除节点是红色
因为删除节点是红色,直接删除即可,不会影响该路径上黑色节点的数量
2.1.2 删除节点是黑色
如果是黑色,导致黑高失衡,需要进行平衡调整
2.1.2.1 删除节点是根结点
删除节点是叶子节点且是根节点,说明整棵树只有一个节点,直接删除就行
2.1.2.2 兄弟节点是红色(有没有可能不存在兄弟节点?不可能,因为删除节点是黑色叶子节点,如果没有兄弟节点,那么左右的黑高一定不相等,违反了红黑树的规则)
既然删除节点不是根节点,那一定有父节点,那就说明一定有兄弟节点,如果兄弟节点是红色
待删除节点是黑色,它的兄弟节点是红色,要想左右黑高相等,红色的兄弟节点下必有子节点且是黑色,这样才能黑高相等。
这里提前做个声明,规定下节点昵称,下面都通用这些昵称
如果待删除节点在右侧,红色兄弟节点在左侧,先把左兄弟节点和右侄节点换色(如果没有右侄节点,那就直接将红兄弟节点变黑),然后再右旋如果待删除节点在左侧,红色兄弟节点在右侧,先把右兄弟节点和左侄节点换色(如果没有左侄节点,那就直接将红兄弟节点变黑),然后再左旋
上述两种情况其实不对,因为没有考虑到红兄弟的红孙子节点,如果有红孙子,那么此时NR下连着红节点,就不符合红黑规则,因此干脆直接用下面的方式,同时适配上面的情况
- 如果删除节点是是黑叶子,兄弟节点是红色,且双侄子都是黑,双侄子有红子节点,此时怎么办?如下图这种情况
上下图的区别就是先变色后旋转还是先旋转后变色,两种方式都可以
2.1.2.3 兄弟节点是黑色
既然删除节点不是根节点,那一定有父节点,那就说明一定有兄弟节点,如果兄弟节点是黑色
2.1.2.3.1 且兄弟有红色子节点
一共有这些种情况
- 兄弟在左,且兄弟有左子节点——兄侄都在左侧(以下两种皆符合)
LL型(左左型):以P为中心,先右旋,后变色(兄B变父P色,爷P孙NL变黑)
- 兄弟在左,且兄弟有右子节点——兄侄相反
LR型(左右型):先左旋,再右旋,侄NR变父P色,父P变黑色
- 兄弟在右,且兄弟有右子节点——兄侄都在右侧
同理不再重复
- 兄弟在右,且兄弟有左子节点——兄侄相反
同理不再重复
2.1.2.3.2 且兄弟无红色子节点
- 父节点是红色
交换父兄节点的颜色,父变黑,兄变红,这样黑高就相等了
- 父节点是黑色
将兄弟节点变红
将父节点当成要删除的节点,按照删除节点的情况进行相应处理,下图这样又变成了上一种情况,交换父兄节点颜色
2.2.删除节点有1个子节点
此时待删除的节点一定是黑色的,其子节点一定为红色,否则无法满足红黑树性质。
将红色子节点的值拷贝到待删除节点上,然后将红色子节点删除掉(转化成4.1.1的情况)
2.3.删除节点有2个子节点
使用前驱节点或后继节点作为待删除节点的替代节点,转化成4.1或4.2的情况
删除练习
答案