AVL树(平衡二叉树)

AVL树本质上是一颗二叉查找树,但是它又具有以下特点:

  • 它是一棵空树或它的左右两个子树的高度差的绝对值不超过1
  • 并且左右两个子树都是一棵平衡二叉树
    什么叫平衡因子balance factor:
    结点的左子树的高度-右子树的高度 BalanceFactor = height(leftSubtree) − height(rightSubtree)

AVL 树如何保持平衡

靠以下4种旋转。

右旋转(叫顺时针旋转更贴切)(树结构是LL类型)

如下图


image.png

首先找到不平衡点T,然后找到T的不平衡方向的子节点L为轴(固定点),顺时针旋转,转动不平衡点T.T大于L,T成为L的右子节点。Y子树,大于L小于T,成为T的左子树。

左旋转(叫逆时针旋转更贴切)(树结构是RR类型) 正好是一个镜像反射
image.png

首先找到不平衡点T,然后找到T的不平衡方向的子节点R为轴(固定点),逆时针旋转,转动不平衡点T.T大于R,T成为R的左子节点。Y子树,大于T小于R,成为T的右子树。

右左旋转(树结构是RL类型)
image.png

首先找到不平衡点T,然后找到T的不平衡方向的子节点R 为轴(固定点),顺时针旋转L,L跑到T和R中间,L的右子树,大于L,小于R,成为R的左子树。然后以L为固定点,逆时针旋转T,T成为L的左子树,Y1成为T的右子树。

左右旋转(树结构是LR类型)
image.png

首先找到不平衡点T,然后找到T的不平衡方向的子节点R 为轴(固定点),逆时针旋转R,R 跑到T和L中间,R的左子树,大于L,小于T,成为L的右子树。然后以R为固定点,顺时针旋转T,T成为R的右子树,Y2成为T的左子树。

从下向上找不平衡线。 比如图3 ,不平衡线,v1---L---R---T .图4 的不平衡线是Y2---R--L---T
以上是四种基本的旋转类型。解决AVL树平衡的问题,基本思路是从下往上,依次找出不平衡点。然后使这个节点对应的树平衡,然后继续向上找不平衡的节点,再继续平衡,是个递归调用的过程。

以下是AVL树的实现代码:

/**
 * AVL树实现的map结构
 * @param <Key>
 * @param <Value>
 */
public class AVLTreeMap <Key extends Comparable,Value>{

    public void put(Key key, Value value) {
        root = put(root, key, value);
    }

    /**
     * 递归调用,先递归put方法,找到插入位置  返回一个节点return new TreeNode(key, value);
     * 假设前一步走得是【root.left = put(root.left, key, value);】这一句代码
     * 那【新节点D】 插入的是root的左节点【注意这个不是类所在的root,而是代码中的root】
     *  ******【后面就是不断update和balance .】***********************************
     * 1  【第一次递归========================】
     * update 方法  ,size左右子树的size+1 ,height 是左子树和右子树的高度
     * 最大值+1 。假设没有右子树,height =2
     * balance方法   直接return.
     *       root1
     *      /
     *    D
     * 2 【第二 次递归========================】
     *  假设原来第二次还是走得左侧。
     *  update之后是 root2的height=3
     *               root2
     *             /
     *        root1
     *      /
     *    D
     *    balance方法  调用rightRotate方法,改变高度和size
     * @param root
     * @param key
     * @param value
     * @return
     */
    private TreeNode put(TreeNode root, Key key, Value value) {
        if (root == null)  return new TreeNode(key, value);
        if (key.compareTo(root.key) < 0) root.left = put(root.left, key, value);
        else if (key.compareTo(root.key) > 0) 
                    root.right = put(root.right, key, value);
        else root.value = value;
        update(root);
        return balance(root);
    }

    private void update(TreeNode tree) {
        tree.size = size(tree.left) + size(tree.right) + 1;
        tree.height = Math.max(height(tree.left), height(tree.right)) + 1;
    }


    private TreeNode rightRotate(TreeNode oldRoot){
        TreeNode newRoot = oldRoot.left;
        oldRoot.left = newRoot.right ;
        update(oldRoot);
        newRoot.right = oldRoot ;
        update(newRoot);
        return  newRoot;
    }

    private TreeNode leftRotate(TreeNode oldRoot){
        TreeNode newRoot = oldRoot.right;
        oldRoot.right = newRoot.left ;
        update(oldRoot);
        newRoot.left = oldRoot ;
        update(newRoot);
        return  newRoot;
    }

    private TreeNode balance(TreeNode root) {
        if (height(root.left) - height(root.right) > 1) {
                if (height(root.left.left) > height(root.left.right)) { //LL
                root = rightRotate(root);
            }
            else { //LR
                root.left = leftRotate(root.left);
                root = rightRotate(root);
            }
        }
        else if (height(root.right) - height(root.left) > 1) {
            if (height(root.right.right) > height(root.right.left)) { //RR
                root = leftRotate(root);
            }
            else { //RL
                root.right = rightRotate(root.right);
                root = leftRotate(root);
            }
        }
        return root;
    }
    private TreeNode min(TreeNode root) {
        if (root.left == null) return root;
        return min(root.left);
    }

    public void remove(Key key) {
        remove(root, key);
    }

    /**
     * 删除方法和BST差不多,分四种情况讨论,左右子树都有的时候,要寻找继承者
     * @param root
     * @param key
     * @return
     */
    private TreeNode remove(TreeNode root, Key key) {
        if (root == null) return null;
        if (key.compareTo(root.key) < 0) {
            root.left = remove(root.left, key);
        }
        else if (key.compareTo(root.key) > 0) {
            root.right = remove(root.right, key);
        }
        else {
            if (root.left == null) return root.right;
            else if (root.right == null) return  root.left;
            else {
                TreeNode successor = min(root.right);
                Key tempKey = root.key;
                root.key = successor.key;
                root.value = successor.value;
                successor.key = tempKey;
                root.right = remove(root.right, tempKey);
            }
        }
        update(root);
        return balance(root);
    }

    private  class TreeNode {
        Key key;
        Value value ;
        TreeNode left,right;
        int height ,size ;

        public TreeNode(Key key, Value value) {
            this.key = key;
            this.value = value;
            this.height = 1;
            this.size =1;
        }
    }
    private TreeNode root;
    private int height(TreeNode tree){
        if (tree == null) return 0;
        return tree.height;
    }

    private int size(TreeNode tree) {
        if (tree == null) return 0;
        return tree.size;
    }

    public void displayTree(){
        Stack globalStack = new Stack();
        globalStack.push(root);
        int treeHeight = height(root);
        int nBlanks = (int) Math.pow(2d,(double)treeHeight);
        boolean isRowEmpty = false;
        System.out.println("=========================================================================");
        while(!isRowEmpty) {
            Stack localStack = new Stack();
            isRowEmpty = true;
            for (int  j =0;j<nBlanks;j++) {
                System.out.print(" ");
            }

            while (!globalStack.isEmpty()) {
                TreeNode temp = (TreeNode)globalStack.pop();
                if (temp!=null) {
                    System.out.print(temp.key);
                    localStack.push(temp.left);
                    localStack.push(temp.right);
                    if (temp.left != null || temp.right !=null) {
                        isRowEmpty = false;
                    }
                }
                else {
                    System.out.print("--");
                    localStack.push(null);
                    localStack.push(null);
                }
                for (int j = 0;j<nBlanks*2-2;j++) {
                    System.out.print(" ");
                }
            }
            System.out.println();
            nBlanks /= 2;
            while (!localStack.isEmpty()) {
                globalStack.push(localStack.pop());
            }
        }
        System.out.println("=========================================================================");
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 一直以来,我都很少使用也避免使用到树和图,总觉得它们神秘而又复杂,但是树在一些运算和查找中也不可避免的要使用到,那...
    24K男阅读 11,709评论 5 14
  • 1. AVL树 AVL树简单来说是带有平衡条件的二叉查找树.传统来说是其每个节点的左子树和右子树的高度最多差1(注...
    fredal阅读 5,785评论 0 4
  • 昨晚给老师表扬了下下。 是在芸社诗词讲座第四课上。 本来我们这些“混听生”是没资格得到老师点评作业的。 但这次玩了...
    今思迟阅读 1,937评论 0 1
  • 刚才逛超市回家的路上一直在想到家了一定要写个简书。 其实吧,我从来不是一个贪心的人,一点点儿幸运的事儿就足够满足我...
    冀竞媛是我女神阅读 3,290评论 3 0
  • 文/心甬 又错过了一天,好像时间间隔还不够用复活卡吧。又得重来! 第二次了,又是到三十多天的时候,莫名其妙的断更了...
    心甬阅读 1,852评论 0 7