AVL树(二叉平衡搜索树)

AVL树的概念:

AVL树是二叉查找树的扩展之一, 它的出现是为了弥补二叉搜索树的不足。

二叉搜索树的缺点:

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。

因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年 发明了一种 解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过 1,即可降低树的高度,从而减少平均搜索长度.

一棵AVL树或者是空树或者是具有以下性质的二叉搜索树:

它的左右子树都是AVL树

左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 ,搜索时间复杂在log以2为底的n。

AVL树节点的定义

代码演示

class AVLTreeNode {

       public AVLTreeNode left = null;    // 节点的左孩子

       public AVLTreeNode right = null;   // 节点的右孩子

       public AVLTreeNode parent = null;  // 节点的双亲

       public int val = 0;

       public int bf = 0;   // 当前节点的平衡因子=右子树高度-左子树的高度

       public AVLTreeNode(int val) {

              this.val = val;

       }

}

AVL树的插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。所以AVL树的插入过程可以分为两步

  1. 按照原二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子

这里我们重点探究”调整节点的平衡因子”

AVL树的旋转

如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据 节点插入位置的不同,AVL树的旋转分为四种

新节点插入较高左子树的左侧---右单旋

在插入元素4(subL)前, AVL树是平衡的, 新节点插入,导元素5(parent)的二叉树不平衡,因此需要平衡元素5(parent)的二叉树,要让元素5(parent)平衡,只能将元素5(parent)的左子树减一层,右子树增加一层,想要达到此效果,只好将左子树往上提,这样5(parent)转下来,因为元素5(parent)比元素4(subL)大,只能将其放元素4(subL)的右子树,旋转完成后,更新节点的平衡因子即可。

而如果subL有右子树,右子树根的值 一定大于subL,小于parent只能将其放在parent的左子树

在旋转过程中 ,有以下几种情况需要考虑:

  1. subL节点的右子树可能存在,也可能不存在
  2. parent可能是根节点,也能是子节点, 如果是根节点,旋转完成后,需要更新根节点, 如是子树, 可能是某个节点的左子树, 也可能是左子树,因此需要更新相应节点.

代码实现

public void rotaRight(TreeNode parent){
    TreeNode subL = parent.left;
    TreeNode subLR = subL.right;
    parent.left = subL.right;
    subL.right = parent;
    if (subLR != null){
        subLR.parent = parent;
    }
    TreeNode Pparent = parent.parent;
    parent.parent =subL;
    if (Pparent == null){
        root = subL;
        root.parent =null;
    }else {
        if (Pparent.right == parent){
            Pparent.right = subL;
        }else {
            Pparent.left = subL;
        }
        subL.parent =Pparent;
    }

    subL.df = 0;
    parent.df = 0;

}

新节点插入较高右子树的右侧---左单旋

实现与右单旋差不多,需要改变参数对应

新节点插入导致, AVL树失去平衡,所有需要对AVL树旋转,将subL往上提,parent插入到subL左侧成为左子树,之后调整平衡因子.

在旋转过程中 ,有以下几种情况需要考虑:

subL节点的左子树可能存在,也可能不存在

parent可能是根节点,也能是子节点, 如果是根节点,旋转完成后,需要更新根节点, 如是子树, 可能是某个节点的左子树, 也可能是左子树,因此需要更新相应节点.

代码实现

public void rotaLeft(TreeNode parent){
    TreeNode subL = parent.right;
    TreeNode subLR = subL.left;
    subL.left = parent;
    parent.right = subLR;

    if (subLR != null){
        subLR.parent = parent;
    }
    TreeNode Pparent = parent.parent;
    parent.parent = subL;
    if (Pparent == null){
        root = subL;
        root.parent = null;
    }else {
        if (Pparent.right == parent){
            Pparent.right = subL;
        }else {
            Pparent.left = subL;
        }
        subL.parent = Pparent;
    }

    subL.df=0;
    parent.df = 0;
}

新节点插入较高左子树的右侧的左右:先左单旋再右单旋【左右双旋】

新节点插入较高左子树的右侧的左右,导致AVL失去平衡,通简单的右旋无法使AVL恢复平衡,想达到平衡先对的AVL树进行左单旋再右单旋

左右双旋具体步骤:
左单旋:将sublR节点往上提,subL插入到sublR的左节点中;

右单旋:在左旋完后,对sublR进行右单旋, 将sublR往上提, 由于sublR的右节点数值一定小于parent,所以将sublR的右子树插入到parent的左子树,parent插入为sublR的右子树, 最后调整平衡因子.

新节点插入较高右子树的左侧的右左:先右单旋再左单旋【右左双旋】

新节点插入较高左子树的右侧的左右,导致AVL失去平衡,通简单的右旋无法使AVL恢复平衡,想达到平衡先对的AVL树进行左单旋再右单旋

     右左双旋具体步骤:

        右单旋: 将sublR节点往上提,subL插入到sublR的右节点中;

       左单旋: 在右旋完后,对sublR进行左单旋, 将sublR往上提, 由于sublR的左节点数值一定大                             于 parent,所以将sublR的左子树插入到parent的右子树,parent插入为sublR的左子树,                        最后调整平衡因子

旋转总结:
在AVL树中插入元素, 有四种方式会导致AVL树失去平衡,总计如下

好了本次的学习分享就到这里了,如果本次分享对你有帮助的话,请点一个免费的赞支持一下作者哦.谢谢啦! 我们下次再见.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值