平衡树AVL

概述

为解决二叉搜索树退化成一张链表的情况,改进出了AVL(取名与作者G.M.Adelson-VelskyE.M.Landis

一颗AVL具备的条件:

  • 必须是一颗BST
  • 每个节点的左右子树高度至多相差1

AVL树的查找、插入、删除等操作在平均和最坏的情况下都是O(logN),得益于其一直在动态的维护平衡性

相关参数

  • 平衡因子

    左子树高度减去右子树高度的值称为该节点的平衡因子BF(Balance Factor)。若BF的绝对值大于1,则表明需要进行调整

  • 最小不平衡子树

    距离插入节点最近的,且平衡因子的绝对值大于1的节点为根的子树

调整方式

  • LL型

    image-20220723165252254

  • RR型

    image-20220723165416390

  • LR型

    image-20220723165429874

  • RL型

    image-20220723165447301

其中LL和RR型就可以看成将中间的节点”拎起来“

LR和RL型就先将拐出来的部分旋转为LL和RR型,再进行对应的操作

代码实现

LL型平衡

private Node<T> llRotate(Node<T> avlNode) {
        Node<T> node = avlNode.leftChild;
        avlNode.leftChild = node.rightChild;
        node.rightChild = avlNode;
        return node;
}

RR型平衡

private Node<T> rrRotate(Node<T> avlNode) {
        Node<T> node = avlNode.rightChild;
        avlNode.rightChild = node.leftChild;
        node.leftChild = avlNode;
        return node;
}

LR型平衡

private Node<T> lrRotate(Node<T> avlNode) {
        avlNode.leftChild = rrRotate(avlNode.leftChild);
        return llRotate(avlNode);
}

RL型平衡

private Node<T> rlRotate(Node<T> avlNode) {
        avlNode.rightChild = llRotate(avlNode.rightChild);
        return rrRotate(avlNode);
}

平衡实现逻辑

private Node<T> balance(Node<T> node) {
    // 左子树比右子树高度大于1以上
    if (getAvlTreeHeight(node.leftChild) - getAvlTreeHeight(node.rightChild) > 1) {
        if (getAvlTreeHeight(node.leftChild.leftChild) >= getAvlTreeHeight(node.leftChild.rightChild)) {
            // 执行LL型调整
            node = llRotate(node);
        } else {
            // 执行LR型调整
            node = lrRotate(node);
        }
    } else if (getAvlTreeHeight(node.rightChild) - getAvlTreeHeight(node.leftChild) > 1) {
        if (getAvlTreeHeight(node.rightChild.rightChild) >= getAvlTreeHeight(node.rightChild.leftChild)) {
            // 执行RR型调整
            node = rrRotate(node);
        } else {
            // 执行RL型调整
            node = rlRotate(node);
        }
    }
    return node;
}

建立AVL逻辑

public void createTree(T data) {
    if (data == null) {
        return;
    }
    root = insert(root, data);
}

private Node<T> insert(Node<T> root, T data) {
    // 根节点为空,说明树为空,则创建一颗树
    if (root == null) {
        return new Node<>(data);
    } else {
        if (compare(root, data) > 0) {
            root.leftChild = insert(root.leftChild, data);
            root = balance(root);
        } else if (compare(root, data) < 0) {
            root.rightChild = insert(root.rightChild, data);
            root = balance(root);
        }
        return root;
    }
}

功能测试

private static final Integer[] arrays = new Integer[]{26, 21, 30, 50, 60, 66, 68, 70};

@Test
public void createAvlTree() {
    AvlTree<Integer> avlTree = new AvlTree<>();
    for (Integer data : arrays) {
        avlTree.createTree(data);
    }
    avlTree.printTree();
}
前序遍历: 26 21 66 50 30 60 68 70 
中序遍历: 21 26 30 50 60 66 68 70 
后序遍历: 21 30 60 50 70 68 66 26 

代价分析

  • 查找:效率很好,平均情况和最坏情况都是O(logN)
  • 插入:每插入一个节点至多需要旋一次旋转。总体时间复杂度为O(logN)
  • 删除:每一次删除最多需要O(logN)次旋转,复杂度为O(logN)

AVL树的结构相当的稳定,故查询效率相当的高。但每次插入或删除节点时都会进行动态的维护平衡,带来了不小i的时间成本。所以当查询和删除频率不高时,采用AVL树可以带来极高的查询效率。但当插入和删除频率较高时,AVL的性能并不理想,此时,我们选择采用红黑树

相关代码可以在 https://github.com/Cheung0-bit/tree-practice 中找到

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bruce_Zhang61

给萌新一点鼓励吧!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值