平衡查找二叉树(AVL树)的讲解与实现(JAVA版本)

本文介绍了二叉查找树的概念及其在数据不均匀时可能导致的效率问题,进而引出平衡查找二叉树(AVL树)的重要性。AVL树通过保持任意节点左右子树高度差不超过1来确保高效查找。文章详细阐述了AVL树的左旋、右旋操作,以及插入节点后的四种调整情况,并简要提到了AVL树在Java中的应用局限性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

如果让我们在一堆带有标识的数据中,去高效查询一个特定标识的数据,我们有几种方法?通常我们都会知道二分查找法,二分查找法确实是一种比较高效的查找算法,但是也有弊端就是使用二分查找的数据必须要先经过排序而且针对链表,树这种数据结构并不能像数组一样根据坐标去直接访问

原理

二叉查找树

那么根据上面所说的情况对树型结构的查找就有人提出了下图的形式即子节点和自己的父节点进行比较,比父节点大的放到父节点右边,比父节点小的放到父节点左边这样我们在查找的时候就不用遍历整个树而且可以做到有方向的查找,效率较高。
查找树
看了上图大家可能就会有发现,此种方法的查找的效率和树的高度有关,这种结构叫做二叉查找树这种算法确实可以将我们查找效率提高不少但是查找二叉树可能会由于数据的不同查询的时间又有较大的变化,因为查找二叉树会有以下的情况:
经过排序的数据
如上图我们可以发现如果我们的数据是经过排序的,那么查找树的结构其实就是链表结构,查找的效率也会退化成链表结构的查找效率

平衡查找二叉树(AVL树)

出于二叉查找树的最坏情况考虑,提出了平衡查找二叉树,特点如下:在平衡查找二叉树中,任何一个根节点的左右子树高度差都不能大于1
那面我们如何保证平衡查找二叉树的特点呢?这就需要我们在插入节点和删除节点时,做一些节点位置上的平衡和处理来维持这个特性

维持这个特性的操作我们称之为左旋和右旋操作(我们只会对最小范围的子树进行操作),下面我们会有实际的讲解:

  • 左旋: 根节点以右子树为轴进行旋转。
    左旋

  • 右旋: 根节点以左子树为轴进行旋转。
    右旋
    一般来说查找二叉树插入一个新节点后会出现以下四种类型:LL型,LR型,RR型,RL型以下是这几种类型的介绍与保持平衡的方法。

  • LL型
    对于插入节点后最小二叉树是LL型我们采用右旋操作:
    LL

  • LR型
    对于插入节点后最小二叉树是LR型我们采用先左旋再右旋操作:
    LR

  • RR型
    对于插入节点后最小二叉树是LL型我们采用左旋操作:
    LL型

  • RL型
    对于插入节点后最小二叉树是LR型我们采用先右旋再左旋操作:
    RR

实现

AVLTree

import java.util.Comparator;

public class AVLTree<K, V> {
   

    //根节点
    private AVLNode<K, V> root;
    //平衡二叉树左树大于右树的最大差值
    private static final int MAX_AVL_ALLOW_LIMIT = 1;
    //平衡二叉树右树大于左树的最大差值
    private static final int MIN_AVL_ALLOW_LIMIT = -1;
    //Key比较器
    private Comparator<K> comparator;
    //树当前大小
    private int size = 1;

    public AVLTree(Comparator<K> comparator) {
   
        this.comparator = comparator;
    }

    public void add(K key, V value) {
   
        AVLNode<K, V> node = new AVLNode<>(key, value);

        //如果root为空,初始化
        if (root == null) {
   
            root = node;
            return;
        }

        //插入
        insert(root, node);

        //平衡二叉树
        balance(node);
    }

    public void remove(K key) {
   
        AVLNode<K, V> node = find(root, key);
        if (node == null) return;

        //最上层节点删除
        if(root == node) {
   
            //左子树为空的情况
            if(node.left == null) root = root.right;
            //右子树为空的情况
            else if(node.right == null) root = root.left;
            else {
   
                AVLNode<K,V> tNode = node.left.right;
                node.left.setNode(node.right, AVLNode.RIGHT_NODE_INDEX);
                node = node.left;
                node.setNode(null, AVLNode.PARENT_NODE_INDEX); //父节点职null
                //左子树挂到右子树的左子树下面
                leftNodeMoveRightNode(node, tNode);
                root = node;
            }
        }
        //叶子结点删除
        else if(node.left == null && node.right == null) {
   
           node.parent.removeLeaf(node.parent.isChildNode(node));
        }
        //根节点被删除,优先左节点替换
        else {
   
            AVLNode<K,V> pNode = node.parent;
            int index = pNode.isChildNode(node);
            pNode.removeLeaf(index);

            if (node.left != null) {
   
                pNode.setNode(node.left, index);

                //如果左子树根节点的右子树不为空
                AVLNode<K, V> rNode = node.left.right;
                if(rNode != null) {
   
                    if(node.right != null) {
   
                        leftNodeMoveRightNode(node, rNode);
                    }
                    node.left.setNode(node.right, AVLNode.RIGHT_NODE_INDEX); //设置右节点
                }
            } else {
   
                //直接设置右节点即可
                pNode.setNode(node.right, index);
            }
        }

        size--;
        //获取root树的最下层叶子结点并进行平衡检验
        balance(findLowestLeafNode(root));
    }

    //左子树挂到右子树的左子树下面
    private void leftNodeMoveRightNode(AVLNode<K, V> node, AVLNode<K, V> tNode) {
   
        AVLNode<K, V> rrlNode = node.right;
        do {
   
            rrlNode = rrlNode.left;
        } while (rrlNode.left 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值