数据结构与算法之AVL平衡树<六>

AVL平衡树是计算机科学中是最先发明的自平衡二叉查找树。
最简单的想法便是要求左右子树具有相同的高度。这种理想的平衡树太过于严格
而难以实现
所以我们必须对其放宽条件。只要其左右子树的高度相差不超过1即可

既然条件放宽了,那我们如何实现呢?
这里就要引入平衡树的一个重要思想那就是调整思想
当我们在按照二叉搜索树的插入方法插入时。如果这个二叉树不满足AVL树的条件
这时候就该对其调整使其满足AVL树的条件

如何调整?

AVL树的基本操作一般涉及运做同在不平衡的二叉查找树所运做的同样的算法。但是要进行预先或随后做一次或多次所谓的“AVL 旋转”
假设由于在二叉排序树上插入结点而失去平衡的最小子树根结点为a(即a是离插入点最近,且平衡因子绝对值超过1的祖先结点),则失去平衡后进行进行的规律可归纳为下列四种情况:

单向右旋平衡处理RR:由于在a的左子树根结点的左子树上插入结点,a的平衡因子由1增至2,致使以a为根的子树失去平衡,则需进行一次右旋转操作;

单向左旋平衡处理LL:由于在a的右子树根结点的右子树上插入结点,a的平衡因子由-1变为-2,致使以a为根的子树失去平衡,则需进行一次左旋转操作;

双向旋转(先左后右)平衡处理LR:由于在a的左子树根结点的右子树上插入结点,a的平衡因子由1增至2,致使以a为根的子树失去平衡,则需进行两次旋转(先左旋后右旋)操作。

双向旋转(先右后左)平衡处理RL:由于在a的右子树根结点的左子树上插入结点,a的平衡因子由-1变为-2,致使以a为根的子树失去平衡,则需进行两次旋转(先右旋后左旋)操作。

了解了这么多的二叉树的方法

我这以Java语言对其实现

AVL树的节点重点内容

public class AvlNode<T> {
    public AvlNode(T date) {
        this(date, null, null);
    }

    public AvlNode(T date, AvlNode<T> left, AvlNode<T> right) {
        super();
        this.date = date;
        this.left = left;
        this.right = right;
        this.hight = 0;
    }

    T date;
    AvlNode<T> left;
    AvlNode<T> right;
    int hight; // 该节点的高度
}

AVL树的实现

ublic class AvlTree<T extends Comparable<? super T>> {

    /*
     * 节点高度
     */
    public int getNodeHight(AvlNode<T> node) {
        return node == null ? 0 : node.hight;
    }

    /*
     * 将节点按照二叉搜索树插入 然后进行调整
     */
    public AvlNode<T> insert(AvlNode<T> node, T x) {
        // 递归出口
        if (node == null) {
            return new AvlNode<T>(x);
        }
        int result = x.compareTo(node.date);
        // 二叉搜索树 左边节点小于根节点 右边节点大于根节点
        if (result < 0) {
            node.left = insert(node.left, x);
        } else if (result > 0)
            node.right = insert(node.right, x);
        else
            ;
        return balance(node);
    }

    // 允许子树之间的高度差为1
    private static final int ALLOWED_IMBALANCE = 1;

    private AvlNode<T> balance(AvlNode<T> node) {
        if (node == null)
            return node;
        // 如果左子树比右子树高度超过一
        if (getNodeHight(node.left) - getNodeHight(node.right) > ALLOWED_IMBALANCE)
            // 如果左子树的左子树的高度大于等于右子树的高度则向左旋转
            if (getNodeHight(node.left.left) >= getNodeHight(node.left.right))
                node = rotateWithLeftChild(node);
            else
                // 如果左子树的右子树的高度大于左子树的高度则向双向右旋转
                node = doubleWithRightChild(node);
        // 如果右子树比左子树高度超过一
        else if (getNodeHight(node.right) - getNodeHight(node.left) > ALLOWED_IMBALANCE)
            // 如果右子树的右子树的高度大于等于左子树的高度则向右旋转
            if (getNodeHight(node.right.right) >= getNodeHight(node.right.left))
                node = rotateWithRightChild(node);
            else
                // 如果左子树的右子树的高度大于左子树的高度则向双向右旋转
                node = doubleWithLeftChild(node);
        // 平衡完成后将高度更新
        node.hight = Math.max(getNodeHight(node.left), getNodeHight(node.right) + 1);
        return node;
    }

    // 单向右旋转
    private AvlNode<T> rotateWithRightChild(AvlNode<T> node) {
        // 翻转节点为 node 和 node.left
        AvlNode<T> nodeR = node.right;
        // 向左翻转
        nodeR.left = node;
        node.right = nodeR.left;
        // 先更新node高度因为node的高度降低了
        node.hight = Math.max(getNodeHight(node.left), getNodeHight(node.right));
        // 更新nodeR nodeR高度上升了
        node.hight = Math.max(getNodeHight(nodeR.left), getNodeHight(nodeR.right));
        return nodeR;
    }

    // 双向右旋转
    private AvlNode<T> doubleWithRightChild(AvlNode<T> node) {
        // 所以先将节点的右树向左旋转使得其右树大于等于左树
        node.right = rotateWithLeftChild(node.right);
        // 然后便可以使用单向左旋转
        return rotateWithLeftChild(node);
    }

    // 单向左旋转
    private AvlNode<T> rotateWithLeftChild(AvlNode<T> node) {
        // 翻转节点为 node 和 node.left
        AvlNode<T> nodeL = node.left;
        // 向左翻转
        nodeL.right = node;
        node.left = nodeL.right;
        // 先更新node高度因为node的高度降低了
        node.hight = Math.max(getNodeHight(node.left), getNodeHight(node.right));
        // 更新nodeL nodeL高度上升了
        node.hight = Math.max(getNodeHight(nodeL.left), getNodeHight(nodeL.right));
        return nodeL;
    }

    // 双向左旋转
    private AvlNode<T> doubleWithLeftChild(AvlNode<T> node) {
        // 双向左旋转的条件是node的左子树的左子树小于右子树
        // 左旋转由于左旋转会将原node的左子树的右子树给旋转后的node的左子树
        // 由于原node的左子树的左子树小于右子树
        // node的高度=原node的左子树的右子树的高度+1大于旋转后的左子树
        // 所以先将节点的左树向右旋转使得其左树大于等于右树
        node.left = rotateWithRightChild(node.left);
        // 然后便可以使用单向左旋转
        return rotateWithLeftChild(node);
    }

    // 移除一个节点
    public AvlNode<T> remove(T x, AvlNode<T> node) {
        // 未找到 
        if (node == null) {
            return node;
        }
        int result = x.compareTo(node.date);
        if (result < 0)
            node.left = remove(x, node.left);
        else if (result > 0)
            node.right = remove(x, node.right);
        //两个子树
        else if (node.left != null && node.right != null) {
            node.date = findMin(node.right);
            node.right = remove(node.date, node.right);
        } else
            //一个子树
            node = node.left == null ? node.right : node.left;
        return balance(node);
    }

    private T findMin(AvlNode<T> node) {
        if (node == null) {
            return null;
        }
        // 递归出口
        else if (node.left == null)
            return node.date;
        return findMin(node.left);
    }

到此AVL平衡树基本实现了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值