数据结构之AVL树

在上一篇博客中,向大家介绍了二叉搜索树这一种数据结构。下面,我们来看一个问题。假设现在有一棵空树,我们先向树中添加一个根节点5,然后再添加6,然后再按顺序添加7、8。我们来看下生成的二叉搜索树是什么样子。

在这里插入图片描述

从上面的图中我们可以看出,二叉搜索树退化成了链表,大大的降低了查询效率。为了解决这个问题,我们引入一种新的数据结构——AVL树。

概述

AVL树是一种带有平衡条件的二叉搜索树。它的左右子树的高度差最多为1。如下图所示,左边的是AVL树,而右边的不是。
在这里插入图片描述

既然AVL树是一颗自平衡的二叉树搜索树,那么我们在二叉搜索树的基础上维护平衡即可。试想一下,什么情况下会破坏平衡条件呢?

  • 插入操作时,向一颗高度差为1的二叉搜索树添加节点,高度差有可能会变成2,破坏了平衡条件。
  • 删除操作时,向一颗高度差为1的二叉搜索树删除节点,高度差有可能会变成2,破坏了平衡条件。

通常,我们可以通过在添加节点和删除节点做一些操作来位置平衡,这种操作我们称之为旋转。

旋转

如果平衡条件破坏在树的外侧(左-左、右-右),那么可以通过一次单旋转来修复平衡。如果平衡条件破坏在树的内存(左-右、右-左),那么需要通过一次双旋转来修复平衡。

  • “左-左”:可以通过一次右旋转来进行修复平衡。
    在这里插入图片描述

  • “右-右”:可以通过一次左旋转来修复平衡。
    在这里插入图片描述

  • “右-左”:需要先将Y节点左旋,形成“左-左”的情况,然后再通过一次右旋来修复平衡。
    在这里插入图片描述

  • “左-右”:需要先将Y节点右旋,先修复成“右-右”的情况,然后再通过一次左旋来修复平衡。
    在这里插入图片描述

代码

public class AVLTree<K extends Comparable<K>, V> {

    //元素个数
    private int size;

    //跟节点
    private Node root;

    public AVLTree() {
        this.size = 0;
        this.root = null;
    }

    private class Node {
        private K key;
        private V value;
        private int height;
        private Node leftChild, rightChild;

        private Node(K key, V value) {
            this(key, value, null, null);
        }

        private Node(K key, V value, Node leftChild, Node rightChild) {
            this.key = key;
            this.value = value;
            this.leftChild = leftChild;
            this.rightChild = rightChild;
            this.height = 1;
        }
    }

    /**
     * 树是否为空
     *
     * @return
     */
    public boolean isEmpty() {
        return size == 0;
    }

    /**
     * 树中元素的个数
     *
     * @return
     */
    public int getSize() {
        return size;
    }

    /**
     * 是否包含node.key = key的节点
     *
     * @param key 查找的key
     * @return
     */
    public boolean contains(K key) {
        checkKey(key);
        return find(root, key) == null;
    }

    /**
     * 查找node.key = key的节点
     *
     * @param key
     * @return
     */
    public V get(K key) {
        checkKey(key);
        Node node = find(root, key);
        return node == null ? null : node.value;
    }

    /**
     * 向树中添加元素
     *
     * @param key
     * @param value
     */
    public void add(K key, V value) {
        checkKey(key);
        root = add(root, key, value);
    }

    /**
     * 在以node为根节点的树中添加节点
     *
     * @param node  递归遍历的根节点
     * @param key   新节点的key
     * @param value 新节点的value
     * @return 添加成功,新树的根节点
     */
    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.leftChild = add(node.leftChild, key, value);
        } else if (key.compareTo(node.key) > 0) {
            //在右子树中添加
            node.rightChild = add(node.rightChild, key, value);
        } else {
            //存在则替换
            node.value = value;
        }
        //节点的高度 = 左右子树的最大值 + 1
        node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;
        return node;
    }

    /**
     * 删除元素
     *
     * @param key 元素的key
     * @return
     */
    public V remove(K key) {
        checkKey(key);
        Node retNode = find(root, key);
        if (retNode != null) {
            root = remove(root, key);
        }
        return retNode == null ? null : retNode.value;
    }

    /**
     * 在以node为根节点的树中删除node.key = key的节点
     *
     * @param node 根节点
     * @param key  删除的key
     * @return 删除后,新树的根节点
     */
    private Node remove(Node node, K key) {
        if (node == null) {
            return null;
        }
        Node retNode = null;
        if (key.compareTo(node.key) < 0) {
            //在左子树中删除
            node.leftChild = remove(node.leftChild, key);
            retNode = node;
        } else if (key.compareTo(node.key) > 0) {
            //在右子树中删除
            node.rightChild = remove(node.rightChild, key);
        } else {
            //查找到删除的节点
            if (node.leftChild == null) {
                Node rightChild = node.rightChild;
                node.rightChild = null;
                retNode = rightChild;
                size--;
            } else if (node.rightChild == null) {
                Node leftChild = node.leftChild;
                node.leftChild = null;
                retNode = leftChild;
                size--;
            } else {
                Node min = findMin(node);
                min.rightChild = remove(node.rightChild, min.key);
                min.leftChild = node.leftChild;
                node.leftChild = node.rightChild = null;
                retNode = min;
                size--;
            }
        }
        if (retNode == null) {
            return null;
        }
        retNode.height = Math.max(getHeight(retNode.leftChild), getHeight(retNode.rightChild)) + 1;
        return balance(retNode);
    }


    /**
     * 将以node为根节点的树构建成平衡二叉树
     *
     * @param node 根节点
     * @return 新的平衡二叉树的根节点
     */
    private Node balance(Node node) {
        int balanceFactor = getBalanceFactor(node);
        if (balanceFactor > 1 && getBalanceFactor(node.leftChild) > 0) {
            //LL
            return rightRotate(node);
        } else if (balanceFactor > 1 && getBalanceFactor(node.rightChild) > 0) {
            //LR 先让左子节点进行左旋,变成LL,然后再右旋
            node.leftChild = leftRotate(node.leftChild);
            return rightRotate(node);
        } else if (balanceFactor < -1 && getBalanceFactor(node.leftChild) > 0) {
            //RL 先让右子节点进行右旋,变成RR,然后再左旋
            node.rightChild = rightRotate(node.rightChild);
            return leftRotate(node);
        } else if (balanceFactor < -1 && getBalanceFactor(node.rightChild) > 0) {
            //RR
            return leftRotate(node);
        }
        return node;
    }


    /**
     * 左旋
     *
     * @param x 旋转的根节点
     * @return 旋转后新的根节点
     */
    private Node leftRotate(Node x) {

        //     x                          y
        //	  / \                       /   \
        //   t1  y        左旋         x     z
        //      / \    ---------->    / \     \
        //     t2  z                 t1 t2     t3
        //  	     \
        //          t3

        Node y = x.rightChild;
        Node t2 = y.leftChild;

        x.rightChild = t2;
        y.leftChild = x;

        //因节点发生变化,重新结算高度
        x.height = Math.max(getHeight(x.leftChild), getHeight(x.rightChild));
        y.height = Math.max(getHeight(y.leftChild), getHeight(y.rightChild));

        return y;
    }

    /**
     * 右旋
     *
     * @param x 旋转的根节点
     * @return 旋转后新的根节点
     */
    private Node rightRotate(Node x) {

        //        x                         y
        //       / \                      /   \
        //      y  t1       右旋         z     x
        //     / \       ---------->    /     / \
        //    z  t2                    t3    t2 t1
        //   / 
        //  t3

        Node y = x.leftChild;
        Node t2 = y.rightChild;

        x.leftChild = t2;
        y.rightChild = x;

        x.height = Math.max(getHeight(x.leftChild), getHeight(x.rightChild));
        y.height = Math.max(getHeight(y.leftChild), getHeight(y.rightChild));

        return y;
    }

    /**
     * 节点的高度
     *
     * @param node
     * @return
     */
    private int getHeight(Node node) {
        if (node == null) {
            return 0;
        }
        return node.height;
    }

    /**
     * 计算节点的平衡因子
     *
     * @param node
     * @return
     */
    private int getBalanceFactor(Node node) {
        return getHeight(node.leftChild) - getHeight(node.rightChild);
    }

    /**
     * 在以node为根节点的树中查找node.key = key的节点
     *
     * @param node 根节点
     * @param key  查找的key
     * @return
     */
    private Node find(Node node, K key) {
        if (node == null) {
            //未找到
            return null;
        }
        if (key.compareTo(node.key) < 0) {
            //在左子树中查找
            return find(node.leftChild, key);
        } else if (key.compareTo(node.key) > 0) {
            //在右子树中查找
            return find(node.rightChild, key);
        } else {
            return node;
        }
    }

    /**
     * 在以node节点为根节点的树中查找key最小的节点
     *
     * @param node
     * @return
     */
    private Node findMin(Node node) {
        if (node.leftChild == null) {
            return node;
        }
        return findMin(node.leftChild);
    }


    /**
     * 校验
     *
     * @param key
     */
    private void checkKey(K key) {
        if (key == null) {
            throw new IllegalArgumentException("key is illegal.");
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值