欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数据结构---平衡二叉树(AVL)

程序员文章站 2022-03-13 10:33:53
...
>AVL树 取自 G.M.Adelson-Velsky 和E.M.Landis

1962年的论文首次提出

满二叉树:除了叶子节点其他酒店都有左孩子和右孩子。
数据结构---平衡二叉树(AVL)
完全二叉树:所有叶子节点最多相差一层。
数据结构---平衡二叉树(AVL)

定义:对于任意一个节点,左子树和柚子树的高度差不能超过1
数据结构---平衡二叉树(AVL)
如上图12 节点 的左子树 8、 5、 4高度为3
12 的左子树 18和 17 高度为2 相差不超过1
8的左子树 5 和 4 高度为 2 ,8 的右子树11 高度为1 相差不超过1
18 的左子树 17 高度为1 18的右子树 高度为 0 相差不超过1

平衡二叉树的高度和节点数量之间的关系也是O(logn)的

平衡因子:左子树高度减去右子树的高度
数据结构---平衡二叉树(AVL)

平衡二叉树是在二分搜索树上进行拓展

	//计算节点的高度
    private int getNodeHeight(Node node){
        if(node == null)
            return 0;
        return node.height;

    }

 	//向二分搜索树添加
    private  Node add(Node node,K key,V value){
        if(node==null){
            size ++;
            return new Node(key,value);
        }
        if(key.compareTo(node.key) > 0)
            node.right = add(node.right,key,value);
        else if(key.compareTo(node.key)<0)
            node.left = add(node.left,key,value);
        else
            node.value = value;
        //每次 插入好 就是上面的逻辑 会把节点插入适合的位置 找到了位置后 计算自己的高度
        node.height = 1 + Math.max(getNodeHeight(node.left),getNodeHeight(node.right));
        //计算平衡因子 找到当前节点的左孩子和右孩子 的 高度相减计算平衡影子
        int balanceFactor = getBalanceFactor(node);
        //如果平衡影子直接高度相差的绝对值大于1
        if(Math.abs(balanceFactor)>1)
        //就认为这不是平衡的
            System.out.println("unbalanced"  + balanceFactor);
        //最后返回这个节点
        return node;
    }
    //计算平衡因子
   private int getBalanceFactor(Node node){
        if(node == null)
            return 0;
        return getNodeHeight(node.left)-getNodeHeight(node.right);
    }
数据结构---平衡二叉树(AVL) 如上图插入 8 < 12 往左边插入 此时12高度为2 平衡影子为1 5<8 往左边插入 此时12 的 高度为3 平衡影子为2 此时已经是不平衡的树了。 数据结构---平衡二叉树(AVL)

如上图的关系可以得出:T1<z<T2<x<T3<y<T4T_1<z<T2<x<T_3<y<T_4
由于yy的平衡影子为2 不满足 平衡二叉树 所以找到yy高度较高的方向 yy的左子树 由于x<yx<y 故可以将 yy作为xx的右子树

右旋转(RR)

数据结构---平衡二叉树(AVL)数据结构---平衡二叉树(AVL)

使x.右子树 = y
同时根据公式可知T3<y<T4T_3<y<T_4 由于T3比y小 故将T3放入 y的左子树。
y.left =t3
数据结构---平衡二叉树(AVL)
上图是一个右旋转的过程 旋转后依然满足二叉树的性质 并且也颗平衡二叉树。

下面上代码

    /**
     * 对节点y进行右旋操作,返回旋转后新的根节点x
     *
     *     //          y                       x
     *     //         / \                    /    \
     *     //        x   T4     右旋(y)      z      y
     *     //       / \       ========>    /  \    / \
     *     //      z   T3                 T1   T2 T3  T4
     *     //     / \
     *     //    T1  T2
     * @param y 传入的是要更新的平衡因子不对的node 也就是↑y
     * @return
     */
    private Node rightRotate(Node y){
        //取出x节点从y的left节点
        Node x = y.left;
        //交换node零食存储t3
        Node T3 =x.left;
        //x的右子树等于y
        x.right = y;
        //y的左子树等于t3
        y.left = T3;
        //由于x,和 y发生了旋转高度需要改变 获取左孩子和右孩子节点高度 重新计算
        y.height = 1 + Math.max(getNodeHeight(y.left),getNodeHeight(y.right));
        x.height = 1 + Math.max(getNodeHeight(y.left),getNodeHeight(y.right));
        //返回 x 和 y 进行旋转后的 node x
        return x;
    }

左旋转(LL)

数据结构---平衡二叉树(AVL)

最旋转和 右旋转是一样的过程
由二叉树性质可得T4<y<T3<x<T1<z<T2T4<y<T3<x<T1<z<T2
跟旋转右子树一样的性质一样
先将取出 x节点
取出t3节点
T4<y<T3<xT4<y<T3<x yyxx小将 yy放入将yy放在xx的左子树上
既然y比T3小 那为什么不放在t3的左子树下面呢 如果这么做 就变成2h+1 当t3取 h+1 时 t3+ y + t4 = h+1 +1 + h = 2h+2 此时T4 没有了 t3 只有1的高度 只有h取到0时才能满足平衡二叉树的性质。

    /**
     * 对节点y进行右旋操作,返回旋转后新的根节点x
     *
     *     //          y                        x
     *     //         /  \                     /   \
     *     //        T4   x     左旋(y)       z    y
     *     //            / \    ======>     /  \ /  \
     *     //           T3  z             T1   T2 T3  T4
     *     //              / \
     *     //            T1  T2
     * @param y 传入的是要更新的平衡因子不对的node 也就是↑y
     * @return
     */
    private Node leftRotate(Node y){
        //取出元素x
        Node x = y.right;
        Node T3 = x.left;
        x.left= y;
        y.right = T3;
        //由于x,和 y发生了旋转高度需要改变 获取左孩子和右孩子节点高度 重新计算
        y.height = 1 + Math.max(getNodeHeight(y.left),getNodeHeight(y.right));
        x.height = 1 + Math.max(getNodeHeight(x.left),getNodeHeight(x.right));
        return x;

    }

数据结构---平衡二叉树(AVL)

如果插入的元素在 左边的 右边如上图 是没法左旋转的 因为 10 和 12 都比8大。

左右旋转(LR)

数据结构---平衡二叉树(AVL)

对于在 y左节点的右节点 插入元素 我们先要将 x进行坐旋转
数据结构---平衡二叉树(AVL)
那么问题就转化为了 上面右旋转的问题了, 对y进行右旋转。

右左旋转(RL)

数据结构---平衡二叉树(AVL)
//LR
        if(balanceFactor>1 && getBalanceFactor(node.left)< 0 ){
            node.left = leftRotate(node.left);
            return rightRotate(node);
        }

跟上面一样先将x进行右旋转
数据结构---平衡二叉树(AVL)

//RL
        if(balanceFactor<-1 && getBalanceFactor(node.right)> 0 ){
            node.right = rightRotate(node.right);
            return leftRotate(node);
        }

以下是 平衡二叉树和二分查找树 做对比
数据结构---平衡二叉树(AVL)
在加载傲慢与偏见这本书 的情况下 似乎 二叉树 会更好一些 这说明 本书单词排序比较随机 没有形成一颗严重失衡 的 二叉树 同时平衡二叉树 需要有不断地左旋 右旋 的去调整树的状态 这方面比二叉树更耗时 。
那让我们再来模拟最坏的情况 把这本书 所有单词 从大到小排序
数据结构---平衡二叉树(AVL)
此时 这二叉树就退化成了只有 右孩子的 链表了 此时查询一个单词 就要遍历所有的单词了。

AVLtree github下载