学习地址1、 学习地址2
1、前言
- 节点的深度:从根节点开始自顶向下逐层累加的。
- 节点的高度:从叶子结点开始自底向上逐层累加的。
- 前序遍历:根左右。
- 中序遍历:左根右。
- 后序遍历:左右根。
- 二叉搜索树删除有两个子结点的节点的时候,此时该节点应该找它右子树上最小的子结点。
- 当二叉搜索树只有单侧的子结点,那么查找的效率会从 O ( l o g N ) O(logN) O(logN)退化到 O ( N ) O(N) O(N)。
- 解决:AVL树(每个节点的左子树和右子树的高度差小于等于1)。
- 虽然AVL树能够解决树退化成链表的问题,但是几乎在每插入一个节点之后,树都会进行左旋和右旋进行调整,使其再次成为一个符合要求的AVL树。那么如果树的插入删除很频繁(HashMap),AVL的性能就很拉胯。
2、红黑树
红黑树是属于二叉搜索树的一个分支。
2.1、性质
- 红黑树并不是一个完美平衡二叉查找树,从图上可以看到,根结点P的左子树显然比右子树高,但左子树和右子树的黑结点的层数是相等的,也即任意一个结点到到每个叶子结点的路径都包含数量相同的黑结点(性质5)。
- 所以我们叫红黑树这种平衡为
黑色完美平衡
。
2.2、操作
- 左旋右旋和AVL树一样。(青岛王卓)
2.2.1、变色
- 节点颜色由红色变为黑色或者由黑变红
2.2.2、左旋
- 以某个结点作为支点(旋转结点),其右子结点变为旋转结点的父结点,右子结点的左子结点变为旋转结点的右子结点,左子结点保持不变。(哪个结点旋转进行左旋,其结点右子树的左子树会被调整到该节点旋转之后的右子树)
2.2.3、右旋
- 以某个结点作为支点(旋转结点),其左子结点变为旋转结点的父结点,左子结点的右子结点变为旋转结点的左子结点,右子结点保持不变。(哪个结点旋转进行右旋,其结点左子树的右子树会被调整到该节点旋转之后的左子树)
2.2.4、查找
- 同二叉搜索树。
2.2.5、插入
- 查找插入的位置
- 插入后自平衡。
- 注意:插入节点,必须为红色,理由很简单,红色在父节点(如果存在)为黑色节点时,红黑树的黑色平衡没被破坏,不需要做自平衡操作。
- 但如果插入结点是黑色,那么插入位置所在的子树黑色结点总是多1,必须做自平衡。
2.3、插入情景
2.3.1、红黑树为空树
此时直接把插入结点作为根结点就行,根据红黑树性质2:根节点必须是黑色。还需要把插入结点设为黑色。
2.3.2、插入结点的Key已存在
直接进行值更新
2.3.3、插入结点的父结点为黑结点
由于插入的结点是红色的,当插入结点的黑色时,并不会影响红黑树的平衡,直接插入即可,无需做自平衡。
2.3.4、插入节点的父节点为红色
红黑树的性质2:根结点是黑色
。如果插入节点的父结点为红结点,那么该父结点不可能为根结点,所以插入结点总是存在祖父结点。这一点很关键,因为后续的旋转操作肯定需要祖父结点的参与。
2.3.4.1、叔叔结点存在并且为红结点
依据红黑树性质4可知,红色节点不能相连 ==> 祖父结点肯定为黑结点;
因为不可以同时存在两个相连的红结点。那么此时该插入子树的红黑层数的情况是:黑红红。显然最简单的处理方式是把其改为:红黑红
处理:
- 将P和U节点改为黑色(爸爸叔叔改为黑色)
- 将PP改为红色(爷爷改为红色)
- 将PP设置为当前节点,进行后续处理(以爷爷为当前结点进行后续处理)
2.3.5、叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的左子结点
注意:单纯从插入前来看,叔叔节点非红即空(NIL节点),否则的话破坏了红黑树性质5,此路径会比其它路径多一个黑色节点。
2.3.5.1、新插入节点,为其父节点的左子节点(LL红色情况)
处理:
- 变颜色:将P设置为黑色,将PP设置为红色
- 对PP节点进行右旋
2.3.5.1、新插入节点,为其父节点的右子节点(LR红色情况)
处理:
- 对P进行左旋
- 将P设置为当前节点,得到LL红色情况
- 按照LL红色情况处理(1.变颜色 2.右旋PP)
2.3.6、叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的右子结点
2.3.6.1、新插入节点,为其父节点的右子节点(RR红色情况)
处理:
- 变颜色:将P设置为黑色,将PP设置为红色
- 对PP节点进行左旋
2.3.6.2、新插入节点,为其父节点的左子节点(RL红色情况)
处理:
- 对P进行右旋
- 将P设置为当前节点,得到RR红色情况
- 按照RR红色情况处理(1.变颜色 2.左旋PP)
2.4、案例演示
原树,插入结点值为7。
step1:插入之后,此时属于2.3.4.1的情况(插入的节点的父结点为红色,且叔叔结点也是红色),那么先进行变色(此时有叔叔结点,并且为红色,那么将父亲结点和叔叔结点染黑,爷爷结点变红,以爷爷为当前结点进行后续处理):
step2:以爷爷为当前结点进行处理,分析此时属于2.3.6.1(父结点为红色,并且叔叔结点不存在或者黑色,且父结点是爷爷结点的左子树),那么此时对以5为根的子树先进行左旋(左旋:以哪个结点进行左旋转,其右子结点作为新的根节点,右子树的左子树作为旋转结点的右子树)
step3:此时5和15都是红色(将以5为根的子树,直接当成一个红5就行),那么此时属于2.3.(父结点是红色,叔叔结点不存在或者是黑色,且父亲结点是爷爷结点的左子树),那么首先进行染色,将父亲结点染红,爷爷结点染黑
step4:因为树的根节点必须要是黑的,那么以19为当前结点进行右旋(右旋:哪个结点进行右旋,其左子结点作为根,同时左子结点的右子树作为旋转结点的左子树),完成平衡。