[数据结构4.8]平衡二叉树

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[数据结构4.8]平衡二叉树相关的知识,希望对你有一定的参考价值。

参考技术A 平衡二叉树(AVL),任意结点的平衡因子的绝对值不超过一(左子树高度-右子树高度)。

高度为h的最小平衡二叉树的结点数 。

利用递归的后序遍历过程:

1、判断左子树是一个平衡二叉树

2、判断右子树是一个平衡二叉树

3、判断以该结点为根的二叉树为平衡二叉树

判断条件:若左子树和右子树均为平衡二叉树,且左子树与右子树高度差的绝对值小于等于1,则平衡。

先插入,正调整,每次调整最小不平衡子树

LL平衡旋转(右单旋转)

原因:在结点A的左孩子的左子树上插入了新结点。

调整方法:右旋操作,将A的左孩子B代替A,将A结点称为B的右子树根结点,而B的原右子树则作为A的左子树。

RR平衡旋转(左单旋转)

原因:在结点A的右孩子的右子树上插入了新结点。

调整方法:左旋操作,将A的右孩子B代替A,将A结点称为B的左子树根结点,而B的原右左树则作为A的右子树。

LR平衡旋转(先左后右双旋转)

原因:在结点A的左孩子的右子树上插入了新结点。

调整方法:先左旋后右旋操作,将A的左孩子B的右孩子结点C代替B,然后再将C结点向上代替A的位置。

RL平衡旋转(先右后左双旋转)

原因:在结点A的右孩子的左子树上插入了新结点。

调整方法:先右旋后左旋操作,将A的右孩子B的左孩子结点C代替B,然后再将C结点向上代替A的位置。

数据结构-平衡二叉树

平衡二叉树的重点在于对不平衡的进行旋转从而使它达到平衡.

下面是我理解的平衡二叉树的操作总结:

平衡因子(BF):
 这是一个描述平衡度的一个量,计算的方式为 左子树的深度-右子树的深度。

我们可以从BF中就能知道左子树和右子树之间的平衡程度。

插入数据

平衡二叉树最复杂的就是将数据插入到树中了,因为要涉及到位置的调整。对于位置的调整在平衡二叉树中成为树的旋转.根据平衡因子的状态分为左旋和右旋.

那什么时候需要进行旋转呢?

这个问题确实值得思考,一般都是在平衡因子的绝对值大于1的时候就需要进行旋转了,也就是说左右子树的深度相差超过了1.

那从什么地方做为跟节点进行旋转呢?

这里有一个术语叫做最小不平衡子树,就是平衡因子绝对值大于1,并且最接近叶子节点的。

我们旋转的时候,只需要对最小不平衡子树进行旋转就可以了。

左旋:

当平衡因子为正数的时候并且大于1的时候,进行左旋,左旋的方式为:
根节点的左子树的右子树作为根节点的右子树,根节点作为左子树的右子树。

 

 

 右旋:

当平衡因子为负数的时候,并且小于-1的时候进行右旋,右旋的方式为:
将根节点的右节点的左子树作为根节点的右子树.
根节点作为跟节点的右节点的左子树。

 

注意: 

上面两种实例根节点和子节点都是同一符号的,所以简单的进行左旋或者右旋就可以了。如下图:

,但是如果我们遇到符号不相同的情况下,需要进行两次旋转,如下图:

 

这种情况下,第一次旋转是将C作为根节点右旋,之后再进行左旋.

我们只需要记住正数的时候进行右旋,负数的时候进行左旋即可。

 上面的原理讲完了,下面是具体的JAVA代码实现:

package com.hotusm.datastructure.tree;

/**
 * @author luqibao
 * @date 2017/3/31
 */
public class AVLTree {


    /**
     * 平衡因子所属的状态
     */
    private static final int LH = 1;   //左边高
    private static final int EH = 0;   //一样高
    private static final int RH = -1;  //右边高


    private TreeNode root;  //树的根


    /**
     * 向平衡二叉树插入节点
     *
     * @param data
     */
    public void insertNode(int data) {

        if (this.root == null) {
            this.root = new TreeNode(null, null, EH, data, null);
            return;
        }
        insertAVLNode(this.root, data);

    }


    /**
     * 进行右旋
     *
     * @param treeNode 需要旋转的根
     */
    /*
     * 右旋
     * 将根的左子树的右子树作为根的左子树
     * 左子树的右子树指向根(最后左子树变为了根,根变为了右子树)
     */
    private void rRotate(TreeNode treeNode) {
        TreeNode root = treeNode.getLeft();
        if (treeNode == this.root) {
            this.root = root;
        }
        treeNode.setLeft(root.getRight());
        root.setRight(treeNode);
    }

    /**
     * 进行左旋
     *
     * @param treeNode 需要旋转的根
     */
    /*
    * 左旋
    * 将根的右子树的左子树作为根的右子树
    * 右子树的左子树指向根(最后右子树变为个根,根变为了左子树)
    * */
    private void lRotate(TreeNode treeNode) {
        TreeNode root = treeNode.getRight();
        if (treeNode == this.root) {
            this.root = root;
        }
        treeNode.setRight(root.getLeft());
        root.setLeft(treeNode);
    }

    /**
     * 左平衡旋转(左边平衡因子太大的原因  需要向右移动)
     *
     * @param root
     */
    private void leftBalance(TreeNode root) {
        TreeNode leftNode = root.getLeft();
        switch (leftNode.getBf()) {
            case LH:
                root.setBf(EH);
                leftNode.setBf(EH);
                rRotate(root);
                break;
            //如果是这种情况  根和左子树平衡因子不是同符号的  所以需要两次的旋转
            case RH:
                TreeNode doubleLeftNode = leftNode.getRight();// 左子树的右子树
                switch (doubleLeftNode.getBf()) {
                    case RH:
                        root.setBf(EH);
                        leftNode.setBf(LH);
                        break;
                    case EH:
                        root.setBf(EH);
                        leftNode.setBf(EH);
                        break;
                    case LH:
                        root.setBf(RH);
                        leftNode.setBf(EH);
                        break;
                }
                doubleLeftNode.setBf(EH);
                lRotate(leftNode);
                rRotate(root);
                break;
        }
    }

    /**
     * 右平衡旋转 右边平衡因子太大的原因
     *
     * @param root
     */
    private void rightBalance(TreeNode root) {

        TreeNode rightNode = root.getRight();

        switch (rightNode.getBf()) {
            case RH:
                root.setBf(EH);
                rightNode.setBf(EH);
                lRotate(root);
                break;
            case LH: //如果是这种情况 那么根和右子树的平衡因子不同符号 所以需要进行两次的旋转
                TreeNode doubleLeftNode = rightNode.getLeft();
                switch (doubleLeftNode.getBf()) {
                    case LH:
                        root.setBf(EH);
                        rightNode.setBf(RH);
                        break;
                    case EH:
                        root.setBf(EH);
                        rightNode.setBf(EH);
                        break;
                    case RH:
                        root.setBf(LH);
                        rightNode.setBf(EH);
                        break;
                }
                doubleLeftNode.setBf(EH);
                rRotate(rightNode);
                lRotate(root);
                break;
        }

    }

    /**
     * 向平衡二叉树插入数据data
     * 如果增高那么返回true  否则返回false
     *
     * @param data
     * @return
     */
    private boolean insertAVLNode(TreeNode root, int data) {
        boolean taller = false;
        if (root.getData() == data) {
            return false;
        }
        if (data < root.getData()) {
            if (root.getLeft()==null){
                TreeNode child=new TreeNode(null,null,EH,data,root);
                root.setLeft(child);
                if (root.getBf()==EH){
                    root.setBf(LH);
                    return true;
                }
                /*
                 * 在child 节点未插入到root的左子树中的时候
                 * root的BF只能有两种情况 EH(左右都没节点) 和 RH(有右叶子节点)
                 * 所以还有一种可能就是当时RH的时候 BF变为EH
                 * */
                root.setBf(EH);
            }else {
                taller=insertAVLNode(root.getLeft(),data);
                if (taller){
                    switch (root.getBf()){
                        case LH:
                            leftBalance(root);
                            taller=false;
                            break;
                        case EH:
                            /*
                             *进行上一级的回溯
                             *      O
                             *    O   O
                             *  O  O
                             */
                            root.setBf(LH);
                            taller=true;
                            break;
                        default:
                            root.setBf(EH);
                            taller=false;
                    }
                }
            }
        } else {
            if (root.getRight()==null){
                TreeNode child=new TreeNode(null,null,EH,data,root);
                root.setRight(child);
                if (root.getBf()==EH){
                    root.setBf(RH);
                }
                /*
                * 和上面的类似,在右子树插入到root之前 root的BF只有两种情况 EH(没有左右节点) LH (只有左叶子节点)
                * */
                root.setBf(EH);
            }else {
                taller=insertAVLNode(root.getRight(),data);
                if (taller){
                    switch (root.getBf()){
                        case RH:
                            rightBalance(root);
                            taller=false;
                            break;
                        case LH:
                            root.setBf(EH);
                            taller=false;
                            break;
                        default:
                        /*
                         *这里会进行回溯到更上的一级进行判断 从而进行旋转
                         *              O
                         *            O   O
                         *               O  O
                         */
                            root.setBf(RH);
                            taller=true;
                    }
                }
            }
        }
        return taller;
    }

    public static void main(String[] args) {
        testInsertAVLTree();
    }

    public static void testInsertAVLTree() {
        AVLTree avlTree = new AVLTree();
        avlTree.insertNode(1);
        avlTree.insertNode(2);
        avlTree.insertNode(3);
        avlTree.insertNode(4);
        avlTree.insertNode(5);

    }

    /**
     * 树的节点
     */
    public static class TreeNode {

        private TreeNode left;
        private TreeNode right;
        private TreeNode parent;
        private int bf; //平衡因子
        private int data;

        public TreeNode(TreeNode left, TreeNode right, int bf, int data, TreeNode parent) {
            this.left = left;
            this.right = right;
            this.bf = bf;
            this.data = data;
            this.parent = parent;
        }

        public TreeNode getLeft() {
            return left;
        }

        public void setLeft(TreeNode left) {
            this.left = left;
        }

        public TreeNode getRight() {
            return right;
        }

        public void setRight(TreeNode right) {
            this.right = right;
        }

        public int getBf() {
            return bf;
        }

        public void setBf(int bf) {
            this.bf = bf;
        }

        public int getData() {
            return data;
        }

        public TreeNode getParent() {
            return parent;
        }

        public void setParent(TreeNode parent) {
            this.parent = parent;
        }

        public void setData(int data) {
            this.data = data;
        }

        @Override
        public String toString() {
            return "TreeNode{" +
                    "left=" + left +
                    ", right=" + right +
                    ", bf=" + bf +
                    ", data=" + data +
                    \'}\';
        }
    }
}

代码中进行了详细的注释,看起来应该是没问题的。

参考:大话数据结构 第八章

以上是关于[数据结构4.8]平衡二叉树的主要内容,如果未能解决你的问题,请参考以下文章

学习数据结构--第四章:树与二叉树(平衡二叉树)

什么是平衡二叉树

数据结构:查找|| 平衡二叉树

数据结构与算法二叉树——平衡二叉树

数据结构—— 树:平衡二叉树

平衡二叉树的算法