数据结构 | 平衡二叉树 AVL

1. 概念

平衡二叉树是一种特殊的二叉排序树,其左右子树都是平衡二叉树。所谓平衡,即节点的左右子树高度差不超过1。

平衡因子=左子树高度-右子树高度
对于平衡二叉树,树中所有节点的平衡因子的取值只能是-1、0、1三个值

2. 节点失衡原因及平衡调整问题

  • 左旋
/**
     *  40  node               50
     *   \           左旋     / \
     *    50  child   -》   40  60
     *   / \                 \
     * 45  60                45
     *
     * 以node为根节点,进行左旋转操作(节点失衡由于右孩子的右子树太高引起)
     * 左旋以后,返回树的新的根节点
     * @param node
     * @return
     */
    public Entry<T> leftRotate(Entry<T> node) {
        //左旋
        Entry<T> child = node.right;
        node.right = child.left;
        child.left = node;
        //更新节点高度值
        node.high = Math.max(height(node.left), height(node.right)) + 1;//先更新孩子
        child.high = Math.max(height(child.left), height(child.right)) + 1;//再更新双亲
        return child;
    }
  • 右旋
/**
     *       40 node            30
     *      /         右旋     / \
     *    30  child    -》   20  40
     *   / \                    /
     * 20  35                 35
     * 以node为根节点,进行右旋转操作(节点失衡由于左孩子的左子树太高引起)
     * 右旋以后,返回树的新的根节点
     * @param node
     * @return
     */
    public Entry<T> rightRotate(Entry<T> node) {
        //右旋
        Entry<T> child = node.left;
        node.left = child.right;
        child.right = node;
        //更新节点高度值
        node.high = Math.max(height(node.left), height(node.right)) + 1;
        child.high = Math.max(height(child.left), height(child.right)) + 1;
        return child;
    }
  • 左平衡
/**
     *    40                  40 node
     *   /        左旋      /          右旋     30
     * 20 node     -》    30  child    -》     / \
     *   \               /                   20  40
     *    30 child     20
     * 以node的左孩子为根节点,进行左-右旋转(左平衡操作)
     * 节点失衡由于左孩子的右子树太高引起
     * @param node
     * @return
     */
    public Entry<T> leftBalance(Entry<T> node) {
        node.left = leftRotate(node.left);
        return rightRotate(node);
    }
  • 右平衡
/**
     *  40                40 node
     *   \         右旋    \           左旋     50
     *   60 node    -》    50  child   -》     / \
     *   /                  \                40  60
     * 50 child              60
     * 以node的右孩子为根节点,进行右-左旋转(右平衡操作)
     * 节点失衡由于右孩子的左子树太高引起
     * @param node
     * @return
     */
    public Entry<T> rightBalance(Entry<T> node) {
        node.right = rightRotate(node.right);
        return leftRotate(node);
    }

3. 算法实现

  • 节点类型
/**
     * 节点类型
     */
    static class Entry<T extends Comparable<T>> {
        private T data;
        private Entry<T> left;
        private Entry<T> right;
        private int high;

        public Entry(T data) {
            this(data, null, null, 1);
        }

        public Entry(T data, Entry<T> left, Entry<T> right, int high) {
            this.data = data;
            this.left = left;
            this.right = right;
            this.high = high;
        }
    }
  • 插入操作
	/**
     * 插入
     * 递归实现,首先从根节点开始,先进行递归,直到找到节点是null的位置
     */
    private Entry<T> insert(Entry<T> root, T val) {
        if (root == null) {
            return new Entry<>(val);
        }
        if (root.data.compareTo(val) > 0) {
            root.left = insert(root.left, val);
            //左子树太高
            if (height(root.left) - height(root.right) > 1) {
                //左子树的左子树太高,进行右旋
                if (height(root.left.left) >= height(root.left.right)) {
                    root = rightRotate(root);
                } else {
                    //左子树的右子树太高,进行左-右旋转操作(左平衡)
                    root = leftBalance(root);
                }
            }
        } else if (root.data.compareTo(val) < 0) {
            root.right = insert(root.right, val);
            //右子树太高
            if (height(root.right) - height(root.left) > 1) {
                //右子树的右子树太高,进行左旋
                if (height(root.right.right) >= height(root.right.left)) {
                    root = leftRotate(root);
                } else {
                    //右子树的左子树太高,进行右-左旋转(右平衡)
                    root = rightBalance(root);
                }
            }
        }
        //更新插入节点高度值
        root.high = Math.max(height(root.left), height(root.right)) + 1;
        return root;
    }
  • 删除
 /**
     * 从root节点开始,找值为val的节点进行删除,删除完成后,返回新的子树的节点
     * @param root
     * @param val
     * @return
     */
    private Entry<T> remove(Entry<T> root, T val) {
        if (root == null) {
            return null;
        }

        //当前节点的值比待删节点的值大,则继续在其左子树中寻找
        if (root.data.compareTo(val) > 0) {
            root.left = remove(root.left, val);
            //检测节点是否失衡
            //右子树太高
            if (height(root.right) - height(root.left) > 1) {
                //右子树的右子树太高,进行左旋
                if (height(root.right.right) >= height(root.right.left)) {
                    root = leftRotate(root);
                } else {
                    //右子树的左子树太高,进行右-左旋转(右平衡)
                    root = rightBalance(root);
                }
            }
        } else if (root.data.compareTo(val) < 0) {  //当前节点的值比待删节点的值小,则继续在其右子树中寻找
            root.right = remove(root.right, val);
            //左子树太高
            if (height(root.left) - height(root.right) > 1) {
                //左子树的左子树太高,进行右旋
                if (height(root.left.left) >= height(root.left.right)) {
                    root = rightRotate(root);
                } else {
                    //左子树的右子树太高,进行左-右旋转操作(左平衡)
                    root = leftBalance(root);
                }
            }
        } else {
            //已找到待删除节点root,首先考虑有两个孩子的情况
            if (root.left != null && root.right != null) {
                /**
                 * 为减少旋转次数,选择左子树高删除前驱,右子树高删除后继
                 */
                //左子树高于右子树 删前驱
                if (height(root.left) > height(root.right)) {
                    Entry<T> pre = root.left;
                    while (pre.right != null) {
                        //使pre指向待删节点的前驱节点
                        pre = pre.right;
                    }
                    //用前驱节点的值覆盖待删除节点的值
                    root.data = pre.data;
                    //继续递归寻找待删除节点的前驱节点
                    root.left = remove(root.left, pre.data);
                } else {  //右子树高于左子树 删后继
                    Entry<T> post = root.right;
                    while (post.left != null) {
                        //使post指向待删节点的后继节点
                        post = post.left;
                    }
                    //用后继节点的值覆盖待删除节点的值
                    root.data = post.data;
                    //继续递归寻找待删除节点的后继节点
                    root.right = remove(root.right, post.data);
                }
            } else {
                if (root.left != null) {
                    return root.left;
                } else if (root.right != null) {
                    return root.right;
                } else {
                    return null;
                }
            }
        }
        //更新删除节点的高度值
        root.high = Math.max(height(root.left), height(root.right)) + 1;
        return root;
    }
  • 查询
private boolean query(Entry<T> root, T val) {
        if (root == null) {
            return false;
        }
        if (root.data.compareTo(val) > 0) {
            return query(root.left, val);
        } else if (root.data.compareTo(val) < 0) {
            return query(root.right, val);
        } else {
            return true;
        }
    }
上一篇:数据结构(二), AVL平衡二叉树


下一篇:java红黑树详解