平衡二叉树(AVL)代码笔记

一棵AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树(空树的高度定义为-1)。
重点在于:
插入和删除可能破坏AVL树的平衡特性,需要进行平衡
分为四种情况:
假设重新平衡的节点为a

  • 需要进行单旋转
    (1)a的左儿子的左子树插入
    (2)a的右儿子的右子树插入

  • 需要进行双旋转
    (3)a的左儿子的右子树插入
    (4)a的右儿子的左子树插入

package com.base;

public class AvlTree<Item extends Comparable<? super Item>> {
    private static class Node<Item> {
        Node(Item item) {
            this(item, null, null);
        }

        Node(Item item, Node<Item> lt, Node<Item> rt) {
            this.item = item;
            this.left = lt;
            this.right = rt;
            this.height = 0;
        }

        private Item item;
        private Node<Item> left;
        private Node<Item> right;
        private int height; // 高度
    }

    private Node<Item> root;

    public AvlTree() {
        root = null;
    }

    public void clear() {
        root = null;
    }

    public boolean isEmpty() {
        return root == null;
    }

    /*
     * 查找最小
     */
    public Item min() throws Exception {
        if (isEmpty()) // 树为空的情况
            throw new Exception();
        return min(root).item;
    }

    private Node<Item> min(Node<Item> t) {
        if (t == null)
            return t;

        while (t.left != null) // 非递归方法
            t = t.left;
        return t;
    }

    /*
     * 查找最大
     */
    public Item max() throws Exception {
        if (isEmpty())
            throw new Exception();
        return max(root).item;
    }

    private Node<Item> max(Node<Item> t) { // 递归方法
        if (t.left == null)
            return t;
        else
            return min(t.left);
    }

    /*
     * 是否包含
     */
    public boolean contains(Item x) {
        return contains(x, root);
    }

    private boolean contains(Item x, Node<Item> t) {
        while (t != null) { // 非递归方式
            int cmp = x.compareTo(t.item);

            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else
                return true; // 匹配
        }
        return false; // 不包含
    }

    // 遍历
    public void printTree() {
        if (isEmpty())
            System.out.println("Empty tree");
        else
            printTree(root);
    }

    private void printTree(Node<Item> t) { // 左根右--中序
        if (t != null) {
            printTree(t.left);
            System.out.println(t.item);
            printTree(t.right);
        }
    }

    /*
     * 插入
     */
    public void insert(Item x) {
        root = insert(x, root);
    }

    private Node<Item> insert(Item x, Node<Item> t) {
        if (t == null) // root为null的情况
            return new Node<>(x, null, null);

        int cmp = x.compareTo(t.item);
        if (cmp < 0)
            t.left = insert(x, t.left);
        else if (cmp > 0)
            t.right = insert(x, t.right);
        else
            ; // 正常二叉树插入
        return balance(t); // 再平衡
    }

    /*
     * 删除
     */
    public void remove(Item x) {
        root = remove(x, root);
    }

    private Node<Item> remove(Item x, Node<Item> t) {
        if (t == null)
            return t;

        int cmp = x.compareTo(t.item);
        if (cmp < 0)
            t.left = remove(x, t.left);
        else if (cmp > 0)
            t.right = remove(x, t.right);
        else {
            if (t.right == null)
                return t.left;
            if (t.left == null)
                return t.right;
            t.item = min(t.right).item;
            t.right = remove(t.item, t.right);
        }
        return balance(t); // 正常二叉树删除后再平衡
    }

    // AVL平衡树
    // 每个节点的左子树和右子树的高度最多差 1
    private static final int ALLOWED_IMBALANCE = 1;

    // 平衡二叉树
    private Node<Item> balance(Node<Item> t) {
        if (t == null)
            return t;

        if (height(t.left) - height(t.right) > ALLOWED_IMBALANCE) // 左儿子
            if (height(t.left.left) >= height(t.left.right))
                t = rotateWithLeftChild(t);  // 左儿子的左子树(单旋转)
            else
                t = doubleWithLeftChild(t);  // 左儿子的右子树(双旋转)
        else if (height(t.right) - height(t.left) > ALLOWED_IMBALANCE) // 右儿子
            if (height(t.right.right) >= height(t.right.left))
                t = rotateWithRightChild(t); // 右儿子的右子树(单旋转)
            else
                t = doubleWithRightChild(t); // 右儿子的左子树(双旋转)

        t.height = Math.max(height(t.left), height(t.right)) + 1; // 更新二叉树高度
        return t;
    }

    /*
     * 二叉树高度--树null,高度为-1
     */
    private int height(Node<Item> t) {
        return t == null ? -1 : t.height;
    }

    /*
     * 把左边的树变成右边的树
     */
    private Node<Item> rotateWithLeftChild(Node<Item> k2) {
        Node<Item> k1 = k2.left;
        k2.left = k1.right;
        k1.right = k2;  // k1 变为根
        k2.height = Math.max(height(k2.left), height(k2.right)) + 1; // k2 高度
        k1.height = Math.max(height(k1.left), k2.height) + 1;   // 根k1 高度
        return k1;
    }

    /*
     * 右边的树变为左边的树
     */
    private Node<Item> rotateWithRightChild(Node<Item> k1) {
        Node<Item> k2 = k1.right;
        k1.right = k2.left;
        k2.left = k1;
        k1.height = Math.max(height(k1.left), height(k1.right)) + 1;
        k2.height = Math.max(height(k2.right), k1.height) + 1;
        return k2;
    }

    /*
     * 左儿子的右子树
     */
    private Node<Item> doubleWithLeftChild(Node<Item> k3) {
        k3.left = rotateWithRightChild(k3.left);
        return rotateWithLeftChild(k3);
    }

    /*
     * 右儿子的左子树
     */
    private Node<Item> doubleWithRightChild(Node<Item> k1) {
        k1.right = rotateWithLeftChild(k1.right);
        return rotateWithRightChild(k1);
    }

    /*
     * 检查二叉树是否平衡
     */
    public void checkBalance() {
        checkBalance(root);
    }

    private int checkBalance(Node<Item> t) {
        if (t == null)
            return -1;

        if (t != null) {
            int hl = checkBalance(t.left);
            int hr = checkBalance(t.right);
            if (Math.abs(height(t.left) - height(t.right)) > 1 || height(t.left) != hl || height(t.right) != hr)
                System.out.println("不平衡");
        }

        return height(t);
    }
}

一点一点补充吧

-

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值