平衡二叉树的原理与Java实现

一、什么是平衡二叉树

平衡二叉树是由前苏联的两位数学家G.M.Adelse-Velskil和E.M.Landis提出,因此一般也称作AVL树,AVL树本质还是一棵二叉查找树,只是在其基础上增加了“平衡”的要求。所谓平衡是指,对AVL树的任意结点来说,其左子树与右子树的高度之差的绝对值不超过1,其中左子树与右子树的高度因子之差称为平衡因子。

二叉查找树,又称二叉排序树,亦称二叉搜索树。是数据结构中的一类。在一般情况下,查询效率比链表结构要高。给定值的比较次数等于给定值节点在二叉排序树中的层数。如果二叉排序树是平衡的,则n个节点的二叉排序树的高度为Log2(n+1),其查找效率为O(Log2n),近似于折半查找。如果二叉排序树完全不平衡,则其深度可达到n,查找效率为O(n),退化为顺序查找。一般的,二叉排序树的查找性能在O(Log2n)到O(n)之间。因此,为了获得较好的查找性能,就要构造一棵平衡的二叉排序树。

二、平衡二叉树的原理

1、平衡因子

平衡因子,当前节点的左子树高度减去右子树高度,即为当前节点的平衡因子。如图所示,虽然这不是一棵平衡二叉树,或者说待平衡的二叉查找树。
图1 平衡因子

2、失衡点

当前树中,平衡因子绝对值大于1的节点,即为当前树的失衡点。如图1中,A节点即为失衡点。
平衡因子大于0,说明左子树高度高于右子树;反之,平衡因子小于0,说明左子树高度低于右子树。

3、旋转

上文提到过,平衡二叉树是在二叉查找树基础上增加了“平衡”的要求。如果仅是无序的去向树中填充数据,我们无法预判到哪些数据会填入树中,如图1中,当ABCDE的树生成完成,我们没有办法去预估数据F的填充,我们的平衡树结构很容易遭到破坏,为了修复树的平衡,我们可以通过对树进行简单的修复来让其重新恢复到平衡,由此引出了旋转的概念。
旋转有两种形式,右旋转和左旋转。

提一下根结点的概念,根结点是树的一个组成部分,也叫树根。所有非空的二叉树中,都有且仅有一个根结点简介。它是同一棵树中除本身外所有结点的祖先,没有父结点。

①右旋转

当平衡因子大于1,此时左子树高度高于右子树,如图所示,为了达到平衡,肯定是想办法将左子树的部分数据移至右子树
图2 左旋转
我们要做的很简单:
1、用失衡点A的左子结点B代替A,即切断A与B的关联关系,并将A的父结点中对于A的指向修改为指向B(图中未画出A的父结点);
2、将左子结点的右子树,即以D为根的树,变为失衡点A的左子树;
3、将修改后的A结点作为原左子结点B的右子树。
图3 右旋转
经过这三个步骤,树重新平衡,平衡因子绝对值全部小于等于1。同时,可以看到数据的存储结构的改变仿佛是将A结点向右侧进行了转动。

②左旋转

左旋转就是右子树高了,通过与右旋转相反的操作,使右子树数据转移至左子树,就不画图讲解了
1、用失衡点的右子结点代替失衡点;
2、将右子结点的左子树,变为失衡点的右子树;
3、将修改后的失衡点作为原右子结点的左子树。

③旋转的原理

旋转的原理其实很简单,拿上文右旋转的图片来举例
对于第一步,如果A还有父结点,我们可以得到 A父结点数据 > A结点数据 > B结点数据 是成立的,如果A是整棵树的根那更好,A与B之间进行右旋,B的左子树与A的右子树结构未发生改变,不会影响数据排序,唯一需要处理的是B的右子树。不要问A的左子树去哪了。
首先二叉查找树的前提是存储的数据可排序,如图片中就以数字为例,除叶子结点以外,每一个结点左子结点的数据都是小于自身的,右子结点的数据都是大于自身的。
那么可以得到 B结点数据 < B右子树任意数据 < A节点数据。那么D或以D为根的树,作为B的右子树和作为A左子树其实都不会对数据结构的有序性造成任何影响。

4、旋转的使用

现在我们回头看 1、平衡因子 中的图一,当我们尝试用右旋转修复树的平衡,却发现仍然不能让他变成一颗平衡二叉树,这是为什么呢?
原来这是以D为根的树导致失衡,那么不论如何旋转,以D为根的树都会导致失衡,那么我们要做的就是破坏D树的结构,让D树把数据吐出来,好让我们有足够数量的数据进行树的平衡。
这时候我们不妨将B结点进行左旋转,然后再去右旋转A结点。

好在这是有规律可言的:
1、我们将失衡点的平衡因子大于1的,也就是左侧树过高的记为L类型;将失衡点的平衡因子小于-1的,也就是右侧树过高的记为R类型;

2、L类型的话,我们再去判断他的左子树平衡因子,若大于0说明左子树的左子树更高,记为LL类型;若小于0说明左子树的右子树更高,记为LR类型
R类型的话,去判断他的右子树平衡因子,分别记为RL与RR;

3、根据不同情况选择对应的旋转方式
LL:右旋转失衡点
RR:左旋转失衡点
LR:先左旋转失衡点的左子结点,再右旋转失衡点
RL:先右旋转失衡点的右子结点,再左旋转失衡点

5、数据的移除

对于平衡二叉树数据的移除,前提条件当然是这棵树是一颗平衡二叉树,如图
图4 平衡二叉树

如果我们要移除C点数据,根据二叉查找树的有序性,我们可以得到
1、C的左子树任意数据 < C自身数据 < C的右子树的任意数据
2、C左子树的最大数据为以E为根树的最右叶子结点,图中为H;C右子树的最小数据为以F为根的最左叶子结点,图中为F

那么,我们为了不破坏树的有序结构,要做的也很简单,使用H的值或者F的值来替代C的值,然后移除H或者F,之后再判断是否需要旋转平衡即可。
图5 平衡二叉树移除数据

三、Java代码实现

参考多篇文章资料自己进行的实现整合,存在冗余也请见谅,存在问题感谢指出

import java.io.IOException;

public class AVLNode<T extends Comparable> {
    AVLNode<T> parent;
    AVLNode<T> leftChild, rightChild;
    T data;
    int height;

    public AVLNode(AVLNode<T> parent, AVLNode<T> leftChild, AVLNode<T> rightChild, T data) {
        this.parent = parent;
        this.leftChild = leftChild;
        this.rightChild = rightChild;
        this.data = data;
        this.height = 1;
    }

    public AVLNode(T data) {
        this(null, null, null, data);
    }

    public AVLNode(AVLNode<T> parent, T data) {
        this(parent, null, null, data);
    }

    public AVLNode() {
        this(null, null, null, null);
    }

    /**
     * 右旋转
     * (1)节点的左孩子代表此节点 
     * (2)节点的左孩子的右子树变为节点的左子树 
     * (3)将此节点作为左孩子节点的右子树。
     *
     * @param node 失衡点
     * @return
     */
    public AVLNode<T> rightRotation(AVLNode<T> node) {
        if (node == null) {
            return null;
        }
        AVLNode<T> leftChild = node.leftChild;
        // 节点的左孩子代表此节点
        if (node.parent != null) {
            if (node.parent.leftChild == node) {
                node.parent.leftChild = leftChild;
            } else if (node.parent.rightChild == node) {
                node.parent.rightChild = leftChild;
            }
        }
        leftChild.parent = node.parent;
        // 节点的左孩子的右子树变为节点的左子树
        node.leftChild = leftChild.rightChild;
        if (leftChild.rightChild != null) {
            leftChild.rightChild.parent = node;
        }
        // 将此节点作为左孩子节点的右子树
        leftChild.rightChild = node;
        node.parent = leftChild;
        // 重新计算节点高度
        node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;
        leftChild.height = Math.max(getHeight(leftChild.leftChild), getHeight(leftChild.rightChild)) + 1;
        return leftChild;
    }

    /**
     * 左旋转
     * @param node
     * @return
     */
    public AVLNode<T> leftRotation(AVLNode<T> node) {
        if (node == null) {
            return null;
        }
        AVLNode<T> rightChild = node.rightChild;
        if (node.parent != null) {
            if (node.parent.leftChild == node) {
                node.parent.leftChild = rightChild;
            } else if (node.parent.rightChild == node) {
                node.parent.rightChild = rightChild;
            }
        }
        rightChild.parent = node.parent;
        node.rightChild = rightChild.leftChild;
        if (rightChild.leftChild != null) {
            rightChild.leftChild.parent = node;
        }
        rightChild.leftChild = node;
        node.parent = rightChild;
        node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;
        rightChild.height = Math.max(getHeight(rightChild.leftChild), getHeight(rightChild.rightChild)) + 1;
        return rightChild;
    }

    public AVLNode<T> put(T data) {
        return put(null, this, data);
    }

    /**
     * 新增数据存储
     * @param parent
     * @param node
     * @param data
     * @return
     */
    public AVLNode<T> put(AVLNode<T> parent, AVLNode<T> node, T data) {
        if (node == null || node.data == null) {
            node = new AVLNode(parent, data);
            return node;
        }
        if (node.data.compareTo(data) > 0) {
            node.leftChild = put(node, node.leftChild, data);
        } else if (node.data.compareTo(data) < 0) {
            node.rightChild = put(node, node.rightChild, data);
        }
        node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild)) + 1;
        // 计算平衡因子判断是否需要旋转平衡
        int balanceFactor = getBalanceFactor(node);
        if (balanceFactor > 1 && getBalanceFactor(node.leftChild) >= 0) {
            node = rightRotation(node);
        } else if (balanceFactor > 1 && getBalanceFactor(node.leftChild) < 0) {
            node.leftChild = leftRotation(node.leftChild);
            node = rightRotation(node);
        } else if (balanceFactor < -1 && getBalanceFactor(node.rightChild) > 0) {
            node.rightChild = rightRotation(node.rightChild);
            node = leftRotation(node);
        } else if (balanceFactor < -1 && getBalanceFactor(node.rightChild) <= 0) {
            node = leftRotation(node);
        }
        return node;
    }

    public AVLNode<T> remove(T data) {
        return remove(this, data);
    }

    /**
     * 移除指定数据
     * @param parent
     * @param data
     * @return
     */
    public AVLNode<T> remove(AVLNode<T> parent, T data) {
        AVLNode<T> node = parent.find(data);
        if (node == null)
            return node;
        // 此处存在冗余,应当修改为左子树最大值为空则用右子树最小值替代,进行相关操作
        if (node.data.compareTo(data) >= 0) {
            AVLNode<T> leftMaxData = findMaxData(node.leftChild);
            leftMaxData.parent.data = leftMaxData.data;
            if (node.leftChild == leftMaxData) {
                leftMaxData.parent.leftChild = leftMaxData.leftChild;
            } else {
                leftMaxData.parent.rightChild = leftMaxData.leftChild;
            }
            if (leftMaxData.leftChild != null) {
                leftMaxData.leftChild.parent = leftMaxData.parent;
            }

        } else {
            AVLNode<T> rightMaxData = findMinData(node.rightChild);
            node.rightChild.data = rightMaxData.data;
            if (node.rightChild == rightMaxData) {
                rightMaxData.parent.rightChild = rightMaxData.rightChild;
            } else {
                rightMaxData.parent.leftChild = rightMaxData.rightChild;
            }
            if (rightMaxData.rightChild != null) {
                rightMaxData.rightChild.parent = rightMaxData.parent;
            }
        }
        parent.height = Math.max(getHeight(parent.leftChild), getHeight(parent.rightChild) + 1);
        int balanceFactor = getBalanceFactor(parent);
        if (balanceFactor > 1 && getBalanceFactor(parent.leftChild) >= 0) {
            parent = rightRotation(parent);
        } else if (balanceFactor > 1 && getBalanceFactor(parent.leftChild) < 0) {
            parent.leftChild = leftRotation(parent.leftChild);
            parent = rightRotation(parent);
        } else if (balanceFactor < -1 && getBalanceFactor(parent.rightChild) > 0) {
            parent.rightChild = rightRotation(parent.rightChild);
            parent = leftRotation(parent);
        } else if (balanceFactor < -1 && getBalanceFactor(parent.rightChild) <= 0) {
            parent = leftRotation(parent);
        }
        return parent;
    }

    public AVLNode<T> find(T data) {
        if (this.data.compareTo(data) > 0) {
            return this.leftChild == null ? null : this.leftChild.find(data);
        } else if (this.data.compareTo(data) < 0) {
            return this.rightChild == null ? null : this.rightChild.find(data);
        } else {
            return this;
        }
    }

    private AVLNode<T> findMaxData(AVLNode<T> node) {
        if (node.rightChild != null) {
            node.height = Math.max(getHeight(node.leftChild), getHeight(node.rightChild) - 1);
            return findMaxData(node.rightChild);
        } else {
            return node;
        }
    }

    private AVLNode<T> findMinData(AVLNode<T> node) {
        if (node.leftChild != null) {
            node.height = Math.max(getHeight(node.leftChild) - 1, getHeight(node.rightChild));
            return findMaxData(node.leftChild);
        } else {
            return node;
        }
    }

    public int getHeight(AVLNode<T> node) {
        if (null == node) {
            return 0;
        } else {
            return node.height;
        }
    }

    // 返回节点的平衡因子
    public int getBalanceFactor(AVLNode<T> node) {
        if (node == null)
            return 0;
        return getHeight(node.leftChild) - getHeight(node.rightChild);
    }

    public static void main(String[] args) throws IOException {
        int[] i = new int[]{50, 25, 75, 12, 37, 45, 44, 46};
        AVLNode<Integer> root = new AVLNode<Integer>();
        for (int j = 0; j < i.length; j++) {
            if (i[j] == 12) {
                System.out.println(1);
            }
            root = root.put(i[j]);
        }
        AVLNode tmp = root.remove(45);
        System.out.println(root.data);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值