二叉查找树/二叉平衡树/红黑树

BST

二叉查找树、二叉搜索树、二叉排序树都是同一个概念,只是名字不同而已。

二叉查找树(BST)具备以下特性:

  1. 左子树上所有结点的值均小于或等于它的根结点的值。
  2. 右子树上所有结点的值均大于或等于它的根结点的值。
  3. 左、右子树也分别为二叉排序树。

缺点:

很有可能退化成单链表,时间复杂度为O(n)。

为了避免这种情况,引入了二叉平衡树和红黑树,它们都是通过建树规则来控制树的层数和节点位置。

AVL

特性:

  1. 首先是一颗二叉查找树。
  2. 任何一个节点的左右子树的高度差最多为1。
  3. 它的左右子树都是AVL树。

1、AVL的恢复平衡过程的三个操作 

当插入和删除节点后的树可能会破坏平衡二叉树的规则,如何恢复平衡?

分为左旋和右旋。

(1)左旋

①旋转之前,首先确定旋转支点(pivot): 从添加的节点开始,不断地往父节点去找不平衡的节点,把第一个不平衡的节点作为旋转支点。

②具体的旋转步骤:

1、把旋转支点E原来的右子节点S向上晋升为根节点;

2、旋转支点E左旋降级,变成新的根节点S的左子节点。

但是当需要晋升的节点有左子节点的话,这时候怎么旋转呢?

1、把旋转支点E原来的右子节点S向上晋升为根节点

2、旋转支点E左旋降级,变成新的根节点S的左子节点

3、S原来的左子节点变成旋转支点E的右子节点。

(2)右旋

①旋转之前,首先确定旋转支点;

②具体的旋转步骤:

1、把旋转支点E原来的左子节点S向上晋升为根节点;

2、旋转支点E右旋降级,变成根节点S的右子节点;

3、如果S有右子节点,则S原来的右子节点变成旋转支点E的左子节点。

2、AVL子树失衡的四大场景

导致AVL失衡的场景就是有限的4个:

  • 左左结构失衡(LL型失衡)

  • 右右结构失衡(RR型失衡)

  • 左右结构失衡(LR型失衡)

  • 右左结构失衡(RL型失衡)

删除元素,也会导致AVL失衡,需要再平衡,但是原理和插入元素是类似的。

这里聚焦 介绍插入元素的平衡过程, 删除元素,不做介绍。

场景1:左左结构失衡(LL型失衡)

当左子树的左子树有节点插入时破坏平衡规则。

一次右旋便可。

场景2:右右结构失衡(RR型失衡)

当右子树的右子树有节点插入时破坏平衡规则。

一次左旋便可。

场景3:左右结构失衡(LR型失衡)

当左子树的右子树有节点插入时破坏平衡规则。

先局部左旋把左右变成左左,然后再整体进行一次右旋。

场景4:右左结构失衡(RL型失衡)

当右子树的左子树有节点插入时破坏平衡规则。

先局部进行右旋把右左变成右右,然后再整体进行左旋。

正是由于这种严格的平衡条件,导致AVL在插入和删除的时候,可能需要频繁地进行旋转来保持平衡,所以AVL树多用于查询, 而不是频繁增加删除的场景。

RBT

红黑树也是一种自平衡二叉查找树,放宽了树的平衡条件

(1)它与AVL树类似,都在添加和删除的时候通过旋转操作保持二叉树的平衡,以求更高效的查询性能。

(2)与AVL树相比,红黑树牺牲了部分平衡性,以换取插入/删除操作时较少的旋转操作,整体来说性能要优于AVL树。

1、红黑规则

可以按照括号里边的分类,记住红黑树的几个原则:

  • 颜色属性)性质1:节点非黑即红

  • 根属性)性质2:根节点一定是黑色

  • 叶子属性)性质3:叶子节点(NIL)一定是黑色

  • 红色属性)性质4:每个红色节点的两个子节点,都为黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)

  • (黑色属性)性质5:任一节点到其每个叶子的所有路径,都包含相同数目的黑色节点。

记忆要点:根叶黑,不红红,黑路同。

(1)黑色属性,可以理解为平衡特征, 如果满足不了平衡特征,就要进行平衡操作。

(2)基于上面的原则,我们一般在插入节点的时候,会将这个节点设置为红色,

原因参照最后一条原则: 红色破坏原则的可能性最小,如果是黑色, 很可能导致这条支路的黑色节点比其它支路的要多1,破坏了平衡。

(3)空间换时间

RBT有点属于一种空间换时间类型的优化,在AVL的节点上,增加了颜色的属性,相当于增加了空间的消耗。通过颜色属性的增加, 换取后面平衡操作的次数减少。

2、红黑树的恢复平衡过程的三个操作

一旦红黑树5个原则有不满足的情况,我们视为平衡被打破,如何恢复平衡?

靠它的三种操作:变色、左旋、右旋

(1)变色

节点的颜色由红变黑或由黑变红。

(2)左旋

红黑树的左旋、右旋操作,AVL树的左旋,右旋操作差不多。

(3)右旋

3、红黑树插入节点情景分析

(1)红黑树的节点结构

红黑树中的节点设计:

class TreeNode<K, V> {
    K key;
    V value;
    TreeNode<K, V> left, right, parent;
    Color color;

    public TreeNode(K key, V value, TreeNode<K, V> parent) {
        this.key = key;
        this.value = value;
        this.parent = parent;
        this.color = Color.RED; // 默认新插入节点为红色
    }
}

默认新插入节点为红色。

场景1:红黑树为空树

将节点的颜色修改为黑色即可。

场景2:插入节点的Key已经存在

将对应的Value更新为新插入节点的值。

场景3:插入节点的父节点为黑色

由于新插入的节点是红色,而父节点是黑色,所以并不会改变路径中黑色节点的数目,因此不需要进行调整,直接插入就行。

场景4:插入节点的父节点为红色

根据性质2:根节点是黑色。

如果插入节点的父节点为红色节点,那么该父节点不可能为根节点,所以插入节点总是存在祖父节点(三代关系)。

根据性质4:每个红色节点的两个子节点一定是黑色的。不能有两个红色节点相连

此时会出现两种状态:

  • 父亲和叔叔为红色

  • 父亲为红色,叔叔为黑色

如图

在这里插入图片描述

只关注这一部分。

首先来看没插入之前,不关注叶子节点,左左这一路径中黑色节点的个数为1,想要在插入新节点时不破坏黑色平衡,即不改变这个路径中黑色节点的个数,所以将爷父插入节点的颜色修改为红黑红,由于父节点的颜色由红色变为黑色,需要关注在右右这一路径中黑色节点的个数,如果叔叔节点是红色,此时可以修改为黑色,则没有破坏黑色平衡,而如果是黑色,则需要旋转。

 场景4.1:父亲和叔叔为红色节点

总结下来就是:

变色处理:黑红红 ==> 红黑红

1.将父节点和叔叔节点改为黑色;

2.将爷爷改为红色;

3.将爷爷设置为当前节点,进行后续处理。

这里有一个细节需要注意:

如果插入节点的爷爷节点为根节点,则需要把根节点重新变成黑色,这样为什么没有问题呢?

因为如果是改变根节点的颜色的话,从任一节点到叶子节点的黑色节点数目都会+1,并不违反性质5。

如果爷爷的父节点是黑色,那么又回到了场景2,无需做任何处理;

但如果爷爷的父节点是红色,则违反红黑树性质了,所以需要将爷爷设置为当前节点,继续插入操作, 作自平衡处理,直到整体平衡为止。

场景4.2:叔叔为黑色,父亲为红色,并且插在父亲的左节点

分为两种情况:

  • LL 红色插入
  • LR 红色插入

叔叔为黑色,并且节点的父亲节点是爷爷节点的左子节点。

在这里插入图片描述

场景4.2.1 LL型失衡

细分场景 1: 新插入节点,为其父节点的左子节点(LL红色情况), 插入后 就是LL 型失衡。

在这里插入图片描述

自平衡处理:

1.变颜色:将父亲节点设置为黑色,将爷爷节点设置为红色;

2.以爷爷节点为支点进行右旋。

在这里插入图片描述

场景4.2.2 LR型失衡

细分场景 2: 新插入节点,为其父节点的右子节点(LR红色情况), 插入后就是LR 型失衡。

在这里插入图片描述

自平衡处理:

1.先将左右变成左左,即以父节点为支点进行左旋;

2.再将父节点设置为当前节点,得到LL红色情况;

3.按照LL红色情况处理(1.变色 2.以爷爷节点为支点进行右旋)。
在这里插入图片描述

情景4.3:叔叔为黑节点,父亲为红色,并且父亲节点是祖父节点的右子节点

在这里插入图片描述

情景4.3.1:RR型失衡

新插入节点,为其父节点的右子节点(RR红色情况)。

在这里插入图片描述

自平衡处理:

1.变色:将父亲节点设置为黑色,将爷爷节点设置为红色;

2.以爷爷节点为支点进行左旋。

在这里插入图片描述

情景4.3.2:RL型失衡

新插入节点,为其父节点的左子节点(RL红色情况)。

在这里插入图片描述

自平衡处理:

1.以父节点为支点进行右旋;

2.将父节点设置为当前节点,得到RR红色情况;

3.按照RR红色情况处理(1.变色 2.以爷爷节点为支点进行左旋)

在这里插入图片描述

总结的一张图:

 

参考博客: 

红黑树(图解+秒懂+史上最全) - 疯狂创客圈 - 博客园 (cnblogs.com)

  • 67
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值