Java版高级数据结构算法 - AVL树

知识的学习在于点滴记录,坚持不懈;知识的学习要有深度和广度,不能只流于表面,坐井观天;知识要善于总结,不仅能够理解,更知道如何表达!

AVL树的定义

BST树最差的情况下,就退化成一条链表了(比如一组有序的元素插入BST树),增删查的时间复杂度就无法达到 O ( l o g 2 n ) O(log_2n) O(log2n),AVL树在BST数的基础上,加入了节点平衡的概念,平衡指的是任意一个节点的左子树和右子树的高度差不能超过1,那么这颗树就是一颗平衡树,否则就要通过既定规则的旋转操作,使节点重新达到平衡状态。

AVL树就是平衡二叉树,AVL树中每一个节点的左右子树高度差不会超过1,下面提供AVL树类型的基本定义:

/**
 * AVL树节点类型
 * @param <T>
 */
class AVLNode<T extends Comparable<T>>{
    private T data;
    private int height;  // 用来记录以当前节点为根节点的树的高度
    private AVLNode<T> left;
    private AVLNode<T> right;

    public AVLNode(T data, int height) {
        this.data = data;
        this.height = height;
        this.left = this.right = null;
    }

    public AVLNode(T data, int height, AVLNode<T> left, AVLNode<T> right) {
        this.data = data;
        this.height = height;
        this.left = left;
        this.right = right;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public AVLNode<T> getLeft() {
        return left;
    }

    public void setLeft(AVLNode<T> left) {
        this.left = left;
    }

    public AVLNode<T> getRight() {
        return right;
    }

    public void setRight(AVLNode<T> right) {
        this.right = right;
    }
}

/**
AVL树的类定义
*/
class AVL<T extends Comparable<T>>{
    private AVLNode<T> root;

    /**
     * 获取节点node的左右子树高度差
     * @param node
     * @return
     */
    public int height(AVLNode<T> node){
        return node == null ? 0 : node.getHeight();
    }

    /**
     * 返回当前节点左右子树的高度最高值
     * @param left
     * @param right
     * @return
     */
    public int maxHeight(AVLNode<T> left, AVLNode<T> right){
        return height(left) > height(right) ? height(left) : height(right);
    }
}

AVL树的四种旋转操作

AVL树为了维护节点平衡,总共包含四种旋转操作,分别是:
1.节点失衡,是由于左孩子的左子树太高造成的,直接进行右旋转操作,代码如下:

/**
 * AVL树的右旋操作,以形参node为根节点进行右旋转,旋转完成以后,把新的根节点返回,
 * 并更新相关节点的高度值(从下往上更新,因为父节点的高度值要依赖子节点进行计算)
 *      40              30
 *    30      =>    20       40
 *  20
 * @param node
 * @return
 */
private AVLNode<T> rotateRight(AVLNode<T> node){
    AVLNode<T> child = node.getLeft();
    node.setLeft(child.getRight());
    child.setRight(node);
    node.setHeight(maxHeight(node.getLeft(), node.getRight()) + 1);
    child.setHeight(maxHeight(child.getLeft(), child.getRight()) + 1);
    return child;
}

2.节点失衡,是由于右孩子的右子树太高造成的,进行左旋转操作,代码如下:

/**
 * AVL树的左旋转操作,以形参node为根节点进行左旋转,旋转完成以后,把新的根节点返回,
 * 并更新相关节点的高度值(从下往上更新,因为父节点的高度值要依赖子节点进行计算)
 *    40                       50         
 *       50         =>    40        60
 *          60
 * @param node
 * @return
 */
private AVLNode<T> rotateLeft(AVLNode<T> node){
    AVLNode<T> child = node.getRight();
    node.setRight(child.getLeft());
    child.setLeft(node);
    node.setHeight(maxHeight(node.getLeft(), node.getRight()) + 1);
    child.setHeight(maxHeight(child.getLeft(), child.getRight()) + 1);
    return child;
}

3.节点失衡,是由于左孩子的右子树太高造成的,进行左-右旋转(也叫左平衡操作)操作,代码如下:

/**
 * AVL树的左平衡 - 左右旋转,先以node的左孩子为根节点进行左旋转操作,再以node为根节点进行右旋
 * 转操作
 *      40              40             30
 *  30        =>      30      =>    20     40 
 *     20           20
 * @param node
 * @return
 */
private AVLNode<T> leftBalance(AVLNode<T> node){
    node.setLeft(rotateLeft(node.getLeft()));
    return rotateRight(node);
}

4.节点失衡,是由于右孩子的左子树太高造成的,进行右-左旋转(也叫右平衡操作)操作,代码如下:

/**
 * AVL树的右平衡 - 右左平衡,先以node的右孩子为根节点进行右旋转操作,再以node为根节点进行左旋
 * 转操作
 *   40                 40                    50
 *        60     =>        50        =>   40        60    
 *     50                     60
 * @param node
 * @return
 */
private AVLNode<T> rightBalance(AVLNode<T> node){
    node.setRight(rotateRight(node.getRight()));
    return rotateLeft(node);
}

AVL树的插入操作

AVL树的插入操作,是在BST树的插入操作上引入相应的旋转操作,代码如下:

/**
 * AVL树插入数据
 * @param val
 */
public void insert(T val){
    this.root = insert(this.root, val);
}

private AVLNode<T> insert(AVLNode<T> root, T val) {
    if(root == null){
        return new AVLNode<>(val, 1);
    }

    if(root.getData().compareTo(val) > 0){
        root.setLeft(insert(root.getLeft(), val));
        // 插入左子树 判断当前节点root是否失衡
        if(height(root.getLeft()) - height(root.getRight()) > 1){
            if(root.getLeft().getData().compareTo(val) > 0){
                // 左孩子的左子树失衡  右旋转操作
                root = rotateRight(root);
            } else {
                // 左孩子的右子树失衡  左右旋转
                root = leftBalance(root);
            }
        }
    } else if(root.getData().compareTo(val) < 0){
        root.setRight(insert(root.getRight(), val));
        // 插入右子树 判断当前节点root是否失衡
        if(height(root.getRight()) - height(root.getLeft()) > 1){
            if(root.getRight().getData().compareTo(val) < 0){
                // 右孩子的右子树失衡  左旋转操作
                root = rotateLeft(root);
            } else {
                // 右孩子的左子树失衡  右左旋转
                root = rightBalance(root);
            }
        }
    } else {
        ;
    }

    // 更新节点高度
    root.setHeight(maxHeight(root.getLeft(), root.getRight()) + 1);
    return root;
}

AVL树的删除操作

AVL树的删除操作,是在BST树的删除操作上引入相应的旋转操作,代码如下:

/**
 * 删除AVL数的节点
 * @param val
 */
public void remove(T val){
    this.root = remove(this.root, val);
}

private AVLNode<T> remove(AVLNode<T> root, T val) {
    if(root == null){
        return null;
    }

    if(root.getData().compareTo(val) > 0){
        root.setLeft(remove(root.getLeft(), val));
        // 判断当前节点root是否失衡
        if(height(root.getRight()) - height(root.getLeft()) > 1){
            if(height(root.getRight().getRight())
                    >= height(root.getRight().getLeft())){
                root = rotateLeft(root); // 右孩子的右子树太高,左旋转操作
            } else {
                root = rightBalance(root); // 右孩子的左子树太高,右平衡操作
            }
        }
    } else if(root.getData().compareTo(val) < 0){
        root.setRight(remove(root.getRight(), val));
        // 判断当前节点root是否失衡
        if(height(root.getLeft()) - height(root.getRight()) > 1){
            if(height(root.getLeft().getLeft())
                    >= height(root.getLeft().getRight())){
                root = rotateRight(root); // 左孩子的左子树太高,右旋转操作
            } else {
                root = leftBalance(root); // 左孩子的右子树太高,左平衡操作
            }
        }
    } else {
        if(root.getLeft() != null && root.getRight() != null){
            // 比较左子树和右子树的高度,谁高删除谁,不用旋转
            if(height(root.getLeft()) > height(root.getRight())){
                // 删除前驱
                AVLNode<T> pre = root.getLeft();
                while(pre.getRight() != null){
                    pre = pre.getRight();
                }
                root.setData(pre.getData());
                root.setLeft(remove(root.getLeft(), pre.getData()));
            } else {
                // 删除后继
                AVLNode<T> last = root.getRight();
                while(last.getLeft() != null){
                    last = last.getLeft();
                }
                root.setData(last.getData());
                root.setRight(remove(root.getRight(), last.getData()));
            }
        } else if(root.getLeft() != null){
            return root.getLeft();
        } else if(root.getRight() != null){
            return root.getRight();
        } else {
            return null;
        }
    }

    // 更新节点高度
    root.setHeight(maxHeight(root.getLeft(), root.getRight()) + 1);
    return root;
}

AVL树的其它代码操作和BST树一样,可参考我写的BST树介绍的文章代码。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值