图解AVL树与Java实现

文章目录

      • 二叉搜索树BST缺陷
      • 平衡二叉查找树AVL
      • AVL树查找
      • AVL树插入
        • 1、过程说明
        • 2、LL情况与RR情况
        • 3、LR情况与RL情况
        • 4、效率分析
      • AVL树删除
        • 1、过程说明
        • 2、四种失衡情况
        • 3、效率分析
      • 示例图简化说明
      • AVL树Java实现

二叉搜索树BST缺陷


在二叉搜索树(Binary Search Tree)中

一个有n个节点的BST,它的最小深度量级为log2n,且最大深度为n。比如下面两个二叉树:

图解AVL树与Java实现_第1张图片

虽然二叉搜索树在最优情况下(完全二叉树)平均查找比较次数为树的深度log2n(二分查找)

但是在极端情况下二叉树退化为线性链表查找时间复杂度就退化为O(n),这都是因为不够平衡导致的,所以需要平衡二叉查找树

平衡二叉查找树AVL


  1. 具有二叉搜索树的全部特性
  2. 每个结点的左子树和右子树的高度差至多等于1,将结点左右子树高度差称为平衡因子

其实应该叫 Balanced BinaryTree,之所以叫AVL树是得名于它的发明者 G.M. Adelson-Velsky 和 E.M. Landis

虽然其平衡的特性使得其查找效率提高了,但是当插入或删除结点的时候可能会打破原有的平衡,此时就需要额外做树的平衡性调整,树的平衡性调整主要是靠的两种操作:左旋与右旋,让我们先来看一下这两种操作的一般情况

左旋操作图解AVL树与Java实现_第2张图片

右旋操作
图解AVL树与Java实现_第3张图片

因为左旋会使旋转结点的左子树高度增加右子树高度减少,右旋会使旋转节点的右子树高度增加左子树高度减少,所以会使得原本本不平衡的树结构恢复平衡

AVL树查找


与普通树的查找操作相同,只不过AVL树是平衡的所以不会出现BST的极端情况,所以其查找时间复杂度:
T ( n ) = O ( l o g 2 n ) T(n)=O(log_2n) T(n)=O(log2n)

AVL树插入


1、过程说明

AVL树的插入分为三部分:

  1. 查找找到应插入的位置(存在key相同结点则替换退出即可)
  2. 插入
  3. 平衡性检查(不平衡则做调整)

总的来说,在AVL树插入后可能会导致不平衡的情况只有四种

当插入新结点导致不平衡时, 我们需要找到距离新节点最近的不平衡结点(最小不平衡子树)来调整

2、LL情况与RR情况

图解AVL树与Java实现_第4张图片图解AVL树与Java实现_第5张图片

解决原因

  • LL情况:通过旋转后,最小不平衡子树的左子树高度减1右子树高度加一,并且整体失衡子树高度恢复到未插入前高度
  • RR情况:通过旋转后,最小不平衡子树的左子树高度加1右子树高度减一,并且整体失衡子树高度恢复到未插入前高度

3、LR情况与RL情况

图解AVL树与Java实现_第6张图片图解AVL树与Java实现_第7张图片

解决原因

  • LR情况:通过旋转后,最小不平衡子树的左子树高度减1右子树高度加一,并且整体失衡子树高度恢复到未插入前高度
  • RL情况:通过旋转后,最小不平衡子树的左子树高度加1右子树高度减一,并且整体失衡子树高度恢复到未插入前高度

这两种情况之所以这么解决是因为

不能直接让失衡结点旋转,就拿LR情况来说:

因为真正决定失衡子树高度的是左孩子的右子树,而直接旋转的话会变为5结点的左子树,这样左子树高度减2右子树加2又会失衡,必须使真正决定失衡子树高度的子树避免这种情况,所以要先左旋调整

LR情况第一次旋转其实转变为了LL情况,RL情况第一次旋转其实是转变为了RR情况

4、效率分析

可以看到任何情况失衡都可以在两次旋转内解决,并且失衡子树会恢复到原本未插入前的高度不会递归影响其祖先节点平衡

插入分为三步:查找插入位置、插入与平衡性调整

查找时间复杂度为O(log2N),插入为O(1),平衡调整因为最多两次所以时间复杂度为常数级O(1)

所以插入时间复杂度为:
T ( n ) = O ( l o g 2 n ) + O ( 1 ) + O ( 1 ) = O ( l o g 2 n ) T(n)=O(log_2n)+O(1)+O(1)=O(log_2n) T(n)=O(log2n)+O(1)+O(1)=O(log2n)

AVL树删除


1、过程说明

AVL树的删除分为两部分:

  1. 查找找到应插入的位置(如果存在key相同结点则替换退出即可)
  2. 删除
    1. 叶子结点:直接删除该结点
    2. 单分支结点:用结点的非空叶结点替代后转而删除非空叶结点
    3. 双分支结点:用结点的中序前驱/后继替代后转而删除中序前驱/后继结点
  3. 平衡性检查(不平衡则做调整)

2、四种失衡情况

无论是删除叶子结点还是单分支结点还是双分支结点最终都会转而删除叶子结点,下面就对删除叶节点后会导致失衡四种情况分析

图解AVL树与Java实现_第8张图片

图解AVL树与Java实现_第9张图片

3、效率分析

删除结点导致失衡,失衡子树调整平衡后高度相比于未删除之前减1,所以可能会递归影响祖先结点平衡,需递归向上继续检查修复

所以删除后失衡就不像 插入一样可以在两次旋转内解决,失衡子树会高度减一会递归影响其祖先节点平衡

删除分为三步:查找删除位置、删除与平衡性调整

查找时间复杂度为O(log2N),删除为O(1),平衡调整因为需要递归维护删除结点到根节点平衡所以量级为O(logN)

所以删除时间复杂度:
T ( n ) = O ( l o g 2 n ) + O ( 1 ) + O ( l o g 2 n ) = O ( l o g 2 n ) T(n)=O(log_2n)+O(1)+O(log_2n)=O(log_2n) T(n)=O(log2n)+O(1)+O(log2n)=O(log2n)

示例图简化说明


为了直观、便于理解,上述平衡调整四种情况的最小不平衡子树都是最简的最小不平衡子树,就拿LL情况来说简化状况如下所示:

图解AVL树与Java实现_第10张图片

understand?

AVL树Java实现


传送门:Gayhub

你可能感兴趣的:(java,数据结构,java,数据结构,avl,后端,二叉树)