数据结构进阶——AVL树

一、AVL树基本概念

1.1定义

如果一颗二叉搜索树的左右子树的高度差的绝对值不超过1(1,0,-1),那么这颗二叉搜索树就叫AVL树。


1.2AVL树的性质

AVL树的左右子树也是一颗AVL树,二叉搜索树是一颗高度平衡的二叉树,它查找值的时间复杂度可以保持在树的高度。


二、AVL树的实现

2.1AVL树节点的定义

static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode parent;//记录当前节点的父亲节点
        public int bf;//平衡因子
        public TreeNode(int val) {
            this.val = val;
        }
    }

**注意:**平衡因子的值为右子树高度减去左子树的高度。


2.2AVL树的插入

AVL树的插入逻辑与二叉搜索树的插入逻辑相同,都是通过与节点比较大小来确定新节点的插入位置,但是对于AVL树,每插入一个节点都需要更新这个节点的父亲节点和这个节点的祖先节点的平衡因子;

如果平衡因子大于1,就需要根据平衡因子的值来确定具体的旋转方法:

<1>左单旋 :新节点插入到较高右子树的右边。

<2>右单旋:新节点插入到较高左子树的左边。

<3>左右双旋:新节点插入到较高左子树的右边。

<4>右左双旋 :新节点插入到较高右子树的左边。

!!! 可以看到,AVL树的插入会涉及到树的旋转操作,也就是说,如果大量插入节点或者大量删除节点都会涉及到树的多次旋转,导致时间复杂度过高,因此AVL树只适合用来存储一些不会频繁更新的元素,对于AVL树,主要利用的是它的查找效率。


2.3AVL树插入代码实现

下面是四种旋转方法示意图:

代码实现:

class MyAvlTree {
    static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;
        public TreeNode parent;//记录当前节点的父亲节点
        public int bf;//平衡因子
        public TreeNode(int val) {
            this.val = val;
        }
    }

    public TreeNode root;//根结点


    //向avl树插入值
    public boolean insert(int val) {
        if (root == null) {
            root = new TreeNode(val);
            return true;
        }
        TreeNode node = new TreeNode(val);
        TreeNode cur = root;
        TreeNode prev = null;//记录cur的父亲节点

        while (cur != null) {
            if (cur.val < val) {
                prev = cur;
                cur = cur.right;
            } else if (cur.val > val) {
                prev = cur;
                cur = cur.left;
            } else {
                return false;
            }
        }

        //判断最后一个节点的值的大小
        if (prev.val < val) {
            prev.right = node;
        } else {
            prev.left = node;
        }

        //插入一个节点后从新插入节点的父亲节点开始调节平衡因子
        node.parent = prev;
        cur = node;
        //调节平衡因子
        while (prev !=null) {//调节到根节点结束
            //通过判断cur是prev的左树还是右树,决定平衡因子++还是--
            if (cur == prev.right) {
                prev.bf++;
            } else {
                prev.bf--;
            }

            if (prev.bf == 0) {
                //说明已经平衡了,不需要再向上调整平衡因子
                break;
            } else if (prev.bf == 1 || prev.bf == -1) {
                //继续向上调整,修改平衡因子
                cur = prev;
                prev = prev.parent;

            } else {
                if (prev.bf == 2) {
                    //说明右树高,需要将右树的节点给一部分给左树
                    if (cur.bf == 1) {
                        //左旋
                        rotateRight(prev);
                    } else {
                        //右左双旋
                        rotateRL(prev);
                    }
                } else {
                    //平衡因子为-2,说明左树高,将左树的节点给一部分给右树
                    if (cur.bf == -1) {
                        //右旋
                        rotateRight(prev);
                    } else {
                        //左右双旋
                        rotateLR(prev);
                    }
                }
                //到这里说明平衡了
                break ;
            }
        }
        return true;
    }

    private void rotateRL(TreeNode prev) {
        TreeNode subL = prev.left;
        TreeNode subLR = subL.right;
        int bf = subLR.bf;

        rotateRL(prev.left);
        rotateRL(prev);

        if (bf == -1) {
            subLR.bf = 0;
            prev.bf = 1;
            subL.bf = 0;
        } else if(bf == 1){
            subLR.bf = 0;
            prev.bf = 0;
            subL.bf = -1;
        }
    }

    private void rotateLR(TreeNode prev) {
        TreeNode subL = prev.left;
        TreeNode subLR = subL.right;
        int bf = subLR.bf;

        rotateLeft(prev.left);
        rotateRight(prev);

        //调整平衡因子
        if (bf == -1) {
            subL.bf = 0;
            prev.bf = 1;
            subLR.bf = 0;
        } else if(bf == 1){
            subLR.bf = 0;
            subL.bf = -1;
            prev.bf = 0;
        }
    }

    //右单旋
    private void rotateRight(TreeNode prev) {
        TreeNode subL = prev.left;
        TreeNode subLR = subL.right;

        prev.left = subLR;
        subL.right = prev;
        prev.parent = subL;
        //没有subLR节点
        if (subLR != null) {
            subLR.parent = prev;
        }

        if (prev == root) {
            //检查prev是否为根结点
            subL.parent = null;
            root = subL;
        } else {
            TreeNode Pparent = prev.parent;//记录prev的父亲节点
            if (Pparent.left == prev) {
                //如果prev是它父亲节点的左节点
                Pparent.left = subL;
                subL.parent = Pparent;
            } else {
                //如果prev是它父亲节点的右节点
                Pparent.right = subL;
                subL.parent = Pparent;
            }
        }
        //修改平衡因子
        prev.bf = 0;
        subL.bf = 0;
    }


    //左单旋
    private void rotateLeft(TreeNode prev) {
        TreeNode subR = prev.right;
        TreeNode subRL = subR.left;

        prev.right = subRL;
        subR.left = prev;
        prev.parent = subR;
        if (subRL != null) {
            subRL.parent = prev;
        }

        if (prev == root) {
            subR.parent = null;
            root = subR;
        } else {
            TreeNode Pparent = prev.parent;
            if (Pparent.left == prev) {
                subR.parent = Pparent;
                Pparent.left = subR;
            } else {
                subR.parent = Pparent;
                Pparent.right = subRL;
            }
        }
        //修改平衡因子
        prev.bf = 0;
        subR.bf = 0;
    }
}

2.4AVL树的验证

如果一个二叉树的节点中没有记录平衡因子,就需要通过递归来计算左右子树高度差,如果不大于1,说明是平衡二叉树,否则不是平衡二叉树。

具体代码如下:

 private boolean isBalance(TreeNode root) {
        if (root == null) return true;

        int leftH = height(root.left);
        int rightH = height(root.right);

//检查平衡因子
        if (rightH - leftH != root.bf) {
            System.out.println(root.val + " 平衡因子异常!");
            return false;
        }
        return Math.abs(leftH - rightH) <= 1 &&
                isBalance(root.left) && isBalance(root.right);
    }

2.5AVL树性能分析

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要 维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合

相关推荐
先鱼鲨生1 小时前
数据结构——栈、队列
数据结构
一念之坤1 小时前
零基础学Python之数据结构 -- 01篇
数据结构·python
IT 青年2 小时前
数据结构 (1)基本概念和术语
数据结构·算法
熬夜学编程的小王2 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
liujjjiyun2 小时前
小R的随机播放顺序
数据结构·c++·算法
Reese_Cool4 小时前
【数据结构与算法】排序
java·c语言·开发语言·数据结构·c++·算法·排序算法
djk88884 小时前
.net将List<实体1>的数据转到List<实体2>
数据结构·list·.net
搬砖的小码农_Sky5 小时前
C语言:结构体
c语言·数据结构
_OLi_7 小时前
力扣 LeetCode 106. 从中序与后序遍历序列构造二叉树(Day9:二叉树)
数据结构·算法·leetcode
我明天再来学Web渗透7 小时前
【SQL50】day 2
开发语言·数据结构·leetcode·面试