红黑树
AVL树追求最优的平衡,左右子树的高度差必须控制在1以内,这也就会导致旋转调整操作会更频繁一点.
而红黑树也是一种平衡树,它比AVL树更简单一点.红黑树只追求相对的平衡,不苛求.
红黑树的高度差可以大于1,但也不会太大(左右子树高度差最大可差一倍),所以效率也更折中实用一点.
红黑树的实测统计性能更优于AVL树(它的调整操作更少),所以平衡树的实现一般都选用红黑树.
红黑树的5个性质 以及 2个推论
>>> 已知:
1.每个节点不是红就是黑色
2.根节点必须是黑色,null也算成黑色
3.不能有父子连在一起的红色(只要出现这种情况就要做调整了)
4.新插入的节点默认都是红色(插入黑色的话没有意义,全是黑色的树没有意义)
5.从任何一个节点出发,到其后代的每个叶子节点(注意这里包括null节点)的每条路径,都有相同数量的黑色节点
>>> 推论:
1:红黑树的左右子树高度差最大不会超过一倍(一边全是黑节点,另一边红黑相间的情况)
2.某个节点如果是单子树的情况,则该父子节点必定是黑红(红黑/黑黑/红红的情况都不满足性质5或3)
>>> 新增和删除操作时,就依靠以上几点来判断分析,就是思考怎么调整才能保证始终满足红黑树的5个性质.
红黑树的失衡判断以及 3种平衡操作
首先定义3种操作:
- 变颜色: 把父节点/叔叔节点/爷爷节点 变成 黑黑红.
- 左旋: 二叉树的通用左旋操作
- 右旋: 二叉树的通用右旋操作
*************************************************************************************
>>> 插入节点:
先找到合适的叶子节点,在其后插入新节点(新增的都是红节点),
然后检查 /当前节点/父亲节点/叔叔节点/ 的颜色:
1.如果父节点是黑的,直接新增即可,因为不会破坏任何红黑树的性质,所以没有任何影响.
2.如果父节点是红的,显然就出现了父子红红的情况,破坏了性质3,就需要调整了,
并且此时可推知: 爷爷节点必定是黑的,而叔叔节点要么不存在,要么是红的.
(此时,只需要在纸上画出那几种可能的情况后就可以自己从中归纳出以下解法了)
- 2.1: 红红红 (叔叔节点存在且必定是红)
操作:
变颜色,然后把指针指向爷爷节点,向上递归检查平衡(这里可以思考下为什么要这么做,同81行)
- 2.2: 红红黑 (叔叔节点不存在,null算黑)
最终可归纳出4种树形: LL/LR/RR/RL (这里和AVL树是一样的,只是多了颜色的变换操作)
* * * *
/ \ / \
/ \ / \
* * * *
/ \ \ /
/ \ \ /
* * * *
LL RR LR RL
操作:
LL: 变颜色+右旋
LR: 变颜色+左旋(变为LL)+右旋
RR: 变颜色+右旋
RL: 变颜色+右旋(变为RR)+左旋
************************************************************************************
>>> 删除节点:
先找到该节点的左子树中最大值的节点M,替换该节点的值,也就是转化成删除M节点(可知M一定没有右节点)
然后检查M的颜色:
- 情况1: M是红色
分析:
直接删除M即可,不会破坏红黑树的任何性质.
- 情况2: M是黑色 (此时主要依靠性质5和推论2来分析归纳删除时的几种情况!)
删除M后,显然会导致M的路径上少一个黑节点,破坏性质5,此时需要调整,
分析:
此时主要依靠性质5来分析,可归纳出删除M时的2种情况:
1.无子节点时:
结合性质5可知: 因为M是黑色,M的父亲F可红可黑,M一定有兄弟节点B,而且B及B的子树里有且只有一个黑节点!
(此时,只需要在纸上画出那几种可能的情况后就可以自己从中归纳出以下解法了)
显然B只会出现下面的2种情况: (也就是看兄弟节点的颜色-_-)
1.1 B是黑色,B下面可能还挂着红色的子节点,
1.2 B是红色,B下面肯定还挂着1个黑色子节点,且这个黑色子节点下面可能还挂着红色子节点,
画完那几种情况的图后可归纳出以下解法:
操作:
1.1.1: MFB是黑黑黑的情况,且B下面没挂红子节点:
把B变成红色,也就是F的子树现在成了黑红的单子树情况,F就恢复成红黑树了,
但是会导致F/F的父节点G向下的路径上少了一个黑节点,所以再把指针指向G,递归向上判断调整.
1.x.x: 除1.1.1的其余的所有情况:
其余的几种情况都不需要向上递归,都是变色+旋转搞定.
2.只有一个子节点:
由推论2可知,单子节点的情况只可能是: M为黑色,子节点为红色的情况.(从M向下走,两边黑色数目要相同=1)
操作:
直接删除M,子节点上位并变成黑色.
删除黑色叶子节点后的几种情况图