平衡树简介、AVL树详解及Java实现

1 平衡树和AVL树

  1. 平衡树(Balance Tree,BT) :任意节点的子树的高度差都小于等于1。常见的符合平衡树的有:B树(多路平衡搜索树)、AVL树(二叉平衡搜索树)等;
  2. AVL树:首先是一棵二叉搜索树,其次是高度平衡的二叉树,树中任意节点的两个子树的高度差都小于等于1;

在这里插入图片描述

2 结点

  1. AVLTree是AVL树对应的类,而AVLTreeNode是AVL树节点,它是AVLTree的内部类;

  2. AVLTree包含了AVL树的根节点,AVL树的基本操作也定义在AVLTree中;

  3. AVLTreeNode包括以下几部分:

    (1)key – 关键字,用来对AVL树的节点进行排序;
    (2)left – 左子结点;
    (3)right – 右子结点;
    (4)height – 高度;

3 旋转

  1. 在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡;
  2. 失去平衡的可以概括为4种姿态:LL(左左),LR(左右),RL(右左),RR(右右);
  3. LL:也称左左,插入或删除一个结点后,根结点的左子树高度-右子树高度=2,且左子树的左子树高度-左子树的右子树高度=1;
  4. LR:也称左右,插入或删除一个结点后,根结点的左子树高度-右子树高度=2,且左子树的右子树高度-左子树的左子树高度=1;
  5. RL:也称右左,插入或删除一个结点后,根结点的右子树高度-左子树高度=2,且右子树的左子树高度-右子树的右子树高度=1;
  6. RR:也称右右,插入或删除一个结点后,根结点的右子树高度-左子树高度=2,且右子树的右子树高度-右子树的左子树高度=1;

在这里插入图片描述
在这里插入图片描述

3.1 LL的旋转

LL失去平衡的情况,可以通过一次右旋转让AVL树恢复平衡。如下图:

在这里插入图片描述

3.2 RR的旋转

RR失去平衡的情况,可以通过一次左旋转让AVL树恢复平衡。如下图:

在这里插入图片描述

3.3 LR的旋转

LR失去平衡的情况,需要经过两次旋转才能让AVL树恢复平衡,即一次左子节点的左旋转+一次右旋转。如下图:

在这里插入图片描述

3.4 RL的旋转

RL失去平衡的情况,需要经过两次旋转才能让AVL树恢复平衡,即一次右子节点的右旋转+一次左旋转。如下图:

在这里插入图片描述

4 完整代码

public class AVLTree<T extends Comparable<T>> {
    private AVLTreeNode<T> mRoot;    // 根结点

    // AVL树的节点(内部类)
    private class AVLTreeNode<T extends Comparable<T>> {
        T key; // 键值
        AVLTreeNode<T> left; // 左子结点
        AVLTreeNode<T> right; // 右子结点
        int height; // 高度

        public AVLTreeNode(T key, AVLTreeNode<T> left, AVLTreeNode<T> right) {
            this.key = key;
            this.left = left;
            this.right = right;
            this.height = 0;
        }
    }

    // 获取AVL树高
    public int height() {
        return height(mRoot);
    }

    // 获取当前结点为根结点对应的树高
    private int height(AVLTreeNode<T> tree) {
        if (tree != null) {
            return tree.height;
        }
        return 0;
    }

    // 获取两值的最大值
    private int max(int a, int b) {
        return a > b ? a : b;
    }

    // 前序遍历AVL树
    public void preOrder() {
        preOrder(mRoot);
    }

    private void preOrder(AVLTreeNode<T> tree) {
        if(tree != null) {
            System.out.print(tree.key + " ");
            preOrder(tree.left);
            preOrder(tree.right);
        }
    }


    // 中序遍历AVL树
    public void inOrder() {
        inOrder(mRoot);
    }

    private void inOrder(AVLTreeNode<T> tree) {
        if(tree != null)
        {
            inOrder(tree.left);
            System.out.print(tree.key + " ");
            inOrder(tree.right);
        }
    }

    // 后序遍历AVL树
    public void postOrder() {
        postOrder(mRoot);
    }

    private void postOrder(AVLTreeNode<T> tree) {
        if(tree != null) {
            postOrder(tree.left);
            postOrder(tree.right);
            System.out.print(tree.key + " ");
        }
    }

    /*
     * (递归实现)查找"AVL树x"中键值为key的节点
     */
    public AVLTreeNode<T> recursiveSearch(T key) {
        return recursiveSearch(mRoot, key);
    }

    private AVLTreeNode<T> recursiveSearch(AVLTreeNode<T> x, T key) {
        if (x == null) {
            return null;
        }
        int cmp = key.compareTo(x.key);
        if (cmp < 0) {
            return recursiveSearch(x.left, key);
        }
        else if (cmp > 0) {
            return recursiveSearch(x.right, key);
        }
        else {
            return x;
        }
    }

    /*
     * (迭代实现)查找"AVL树x"中键值为key的节点
     */
    public AVLTreeNode<T> iterativeSearch(T key) {
        return iterativeSearch(mRoot, key);
    }

    private AVLTreeNode<T> iterativeSearch(AVLTreeNode<T> x, T key) {
        while (x!=null) {
            int cmp = key.compareTo(x.key);

            if (cmp < 0)
                x = x.left;
            else if (cmp > 0)
                x = x.right;
            else
                return x;
        }
        return x;
    }

    /*
     * 求最小结点的键值
     */
    public T minimum() {
        AVLTreeNode<T> p = minimum(mRoot);
        if (p != null)
            return p.key;
        return null;
    }

    private AVLTreeNode<T> minimum(AVLTreeNode<T> tree) {
        if (tree == null)
            return null;

        while(tree.left != null)
            tree = tree.left;
        return tree;
    }

    /*
     * 求最大结点的键值
     */
    public T maximum() {
        AVLTreeNode<T> p = maximum(mRoot);
        if (p != null)
            return p.key;

        return null;
    }

    private AVLTreeNode<T> maximum(AVLTreeNode<T> tree) {
        if (tree == null)
            return null;

        while(tree.right != null)
            tree = tree.right;
        return tree;
    }

    /*
     * LL:左左对应的情况(右旋转)。
     */
    private AVLTreeNode<T> leftLeftRotation(AVLTreeNode<T> k1) {
        AVLTreeNode<T> k2;

        k2 = k1.left;
        k1.left = k2.right;
        k2.right = k1;

        k1.height = max(height(k1.left), height(k1.right)) + 1;
        k2.height = max(height(k2.left), height(k2.right)) + 1;

        return k2;
    }

    /*
     * RR:右右对应的情况(左旋转)。
     */
    private AVLTreeNode<T> rightRightRotation(AVLTreeNode<T> k1) {
        AVLTreeNode<T> k2;

        k2 = k1.right;
        k1.right = k2.left;
        k2.left = k1;

        k1.height = max(height(k1.left), height(k1.right)) + 1;
        k2.height = max(height(k2.left), height(k2.right)) + 1;

        return k2;
    }

    /*
     * LR:左右对应的情况(先左子节点左旋后右旋)。
     */
    private AVLTreeNode<T> leftRightRotation(AVLTreeNode<T> k3) {
        k3.left = rightRightRotation(k3.left);

        return leftLeftRotation(k3);
    }

    /*
     * RL:右左对应的情况(先右子节点右旋后左旋)。
     */
    private AVLTreeNode<T> rightLeftRotation(AVLTreeNode<T> k1) {
        k1.right = leftLeftRotation(k1.right);

        return rightRightRotation(k1);
    }

    /*
     * 将新结点插入到AVL树中
     */
    public void insert(T key) {
        mRoot = insert(mRoot, key);
    }

    private AVLTreeNode<T> insert(AVLTreeNode<T> tree, T key) {
        if (tree == null) {
            // 新建节点
            tree = new AVLTreeNode<T>(key, null, null);
        } else {
            int cmp = key.compareTo(tree.key);

            if (cmp < 0) { // 将key插入到tree的左子树
                tree.left = insert(tree.left, key);
                // 插入节点后,若AVL树失去平衡,则进行相应的调节
                if (height(tree.left) - height(tree.right) == 2) {
                    if (key.compareTo(tree.left.key) < 0)
                        tree = leftLeftRotation(tree);
                    else
                        tree = leftRightRotation(tree);
                }
            }
            else if (cmp > 0) { // 将key插入到tree的右子树
                tree.right = insert(tree.right, key);
                // 插入节点后,若AVL树失去平衡,则进行相应的调节
                if (height(tree.right) - height(tree.left) == 2) {
                    if (key.compareTo(tree.right.key) > 0)
                        tree = rightRightRotation(tree);
                    else
                        tree = rightLeftRotation(tree);
                }
            }
            else { // cmp == 0
                System.out.println("添加失败:不允许添加相同的节点!");
            }
        }

        tree.height = max(height(tree.left), height(tree.right)) + 1;
        return tree;
    }


    /*
     * 删除键值为k的结点
     */
    public void remove(T key) {
        AVLTreeNode<T> z = recursiveSearch(mRoot, key);

        if (z != null)
            mRoot = remove(mRoot, z);
    }

    private AVLTreeNode<T> remove(AVLTreeNode<T> tree, AVLTreeNode<T> z) {
        int cmp = z.key.compareTo(tree.key);
        if (cmp < 0) { // 待删除的节点在"tree的左子树"中
            tree.left = remove(tree.left, z);
            // 删除节点后,若AVL树失去平衡,则进行相应的调节
            if (height(tree.right) - height(tree.left) == 2) {
                AVLTreeNode<T> r = tree.right;
                if (height(r.left) > height(r.right))
                    tree = rightLeftRotation(tree);
                else
                    tree = rightRightRotation(tree);
            }
        } else if (cmp > 0) { // 待删除的节点在"tree的右子树"中
            tree.right = remove(tree.right, z);
            // 删除节点后,若AVL树失去平衡,则进行相应的调节
            if (height(tree.left) - height(tree.right) == 2) {
                AVLTreeNode<T> l =  tree.left;
                if (height(l.right) > height(l.left))
                    tree = leftRightRotation(tree);
                else
                    tree = leftLeftRotation(tree);
            }
        } else { // tree是对应要删除的节点
            // tree的左右孩子都非空
            if ((tree.left!=null) && (tree.right!=null)) {
                if (height(tree.left) > height(tree.right)) {
                    // 如果tree的左子树比右子树高;
                    // 则(01)找出tree的左子树中的最大节点
                    //   (02)将该最大节点的值赋值给tree
                    //   (03)删除该最大节点
                    // 采用这种方式的好处是:删除"tree的左子树中最大节点"之后,AVL树仍然是平衡的
                    AVLTreeNode<T> max = maximum(tree.left);
                    tree.key = max.key;
                    tree.left = remove(tree.left, max);
                } else {
                    // 如果tree的左子树不比右子树高(即它们相等,或右子树比左子树高1)
                    // 则(01)找出tree的右子树中的最小节点
                    //   (02)将该最小节点的值赋值给tree
                    //   (03)删除该最小节点
                    // 采用这种方式的好处是:删除"tree的右子树中最小节点"之后,AVL树仍然是平衡的
                    AVLTreeNode<T> min = minimum(tree.right);
                    tree.key = min.key;
                    tree.right = remove(tree.right, min);
                }
            } else {
                tree = (tree.left!=null) ? tree.left : tree.right;
            }
        }

        return tree;
    }

    /*
     * 打印AVL树
     *
     * key        -- 节点的键值
     * direction  --  0,表示该节点是根节点;
     *               -1,表示该节点是它的父结点的左孩子;
     *                1,表示该节点是它的父结点的右孩子。
     */
    public void print() {
        if (mRoot != null)
            print(mRoot, mRoot.key, 0);
    }

    private void print(AVLTreeNode<T> tree, T key, int direction) {
        if(tree != null) {
            if(direction==0) // tree是根节点
                System.out.printf("%d is root\n", tree.key);
            else // tree是分支节点
                System.out.printf("%d is %d's %s child\n", tree.key, key, direction==1?"right" : "left");

            print(tree.left, tree.key, -1);
            print(tree.right,tree.key,  1);
        }
    }

    // 主函数
    public static void main(String[] args) {
        AVLTree<Integer> a = new AVLTree<>();
        a.insert(1);a.insert(2);a.insert(3);a.insert(4);a.insert(5);
//        a.remove(2);
//        a.remove(4);
        System.out.println(a.height());
        a.print();
    }
}
3
2 is root
1 is 2's left child
4 is 2's right child
3 is 4's left child
5 is 4's right child
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hellosc01

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值