X16数据结构部分09

X16数据结构部分09

平衡二叉树概述以及左旋转实现思路

/*
    平衡二叉树(AVL树)概述
        解决了二叉排序树的一些问题
        举一个栗子
        给你1个数列{1,2,3,4,5,6}
        让你构造1棵二叉排序树
            根据左小右大原则
            你就会构造出来只有1直向右子树延申的
            所有节点最多只有1棵节点的二叉树
            也就是左子树全部为空

        此时你会发现
        你所构造的二叉排序树更像是1棵单链表
            这样的话,插入和删除速度不会受影响
            但查询速度会很慢
            甚至比单链表还要慢
            因为在查询的时候
            还需要判断左子树是否有节点

    解决方案
        平衡二叉树
        特点
            前提是二叉排序树
            是1棵空树或2棵子树的高度差绝对值不超过1
            并且左右2子树都是1棵平衡二叉树
            常见的实现方法有红黑树,替罪羊树,AVL算法,伸展树等;

    需求
        给你1个数列,要求创建1棵平衡二叉树
        {4,3,6,5,7,8}

    左旋转思路分析
        一旦右子树的高度 - 左子树的高度 > 1
        就采用左旋转
        降低右子树的高度

    如何进行左旋转
        1. 创建1个新节点,value等于当前根节点的值,创建1个当前指针,指向根节点
        2. 把新节点((NODE)value = 4)的左子树设置为当前节点((ROOT)value = 4)的左子树
        3. 把新节点的右子树设置为当前节点的右子树的左子树,如果没有指向null就行了
        4. 把当前节点的值换成右子节点的值((ROOT)value = 6),注意只是换,还没开始移动呢
        5. 把当前节点的右子树设置为当前节点右子树的右子树((ROOT)value = 6).rightNode = ROOT.r.r
        6. 当前节点的左子树指向新节点((NODE)value = 4)

        左旋转过后
            明显地发现原先根节点指向的右子树
            这条线已经断掉了
            实际上原来的右子树已经被销毁了
            新出现相同的节点只是副本而已
            想想有一种悲伤的感觉
        代码实现的话
            我会复用之前的Node1节点类
     */

树的高度求解

// 将以下方法写进Node类,创建AVLT平衡二叉树类,将原先的二叉排序树类复制进class AVLT,这里就不展示源码了
	/**
     * 返回左子树的高度
     * 递归出口
     *
     * @return 左子树的高度
     */
    public int leftTreeHeight() {
        if (leftNode == null) {
            return 0;
        }
        return leftNode.maximumHeightOfTree();
    }

    /**
     * 返回右子树的高度
     * 递归出口
     *
     * @return 右子树的高度
     */
    public int rightTreeHeight() {
        if (rightNode == null) {
            return 0;
        }
        return rightNode.maximumHeightOfTree();
    }

    /**
     * 树的最大高度
     *
     * @return 当前二叉树的最大高度
     */
    public int maximumHeightOfTree() {
        /*
        程序进入方法体
        递归左子树和右子树
        再取最大值
        还要+1的原因是当前自己
        也要加上
         */
        return Math.max(leftNode == null ? 0 : leftNode.maximumHeightOfTree(),
                rightNode == null ? 0 : rightNode.maximumHeightOfTree()) + 1;
    }

左右旋转的方法

/**
     * 左旋转方法
     */
    public void rotateLeft() {
        /*
        程序开始进行左旋转操作
        
        1. 创建1个新节点,value等于当前根节点的值,创建1个当前指针,指向根节点
        2. 把新节点((NODE)value = 4)的左子树设置为当前节点((ROOT)value = 4)的左子树
        3. 把新节点的右子树设置为当前节点的右子树的左子树,如果没有指向null就行了
        4. 把当前节点的值换成右子节点的值((ROOT)value = 6),注意只是换,还没开始移动呢
        5. 把当前节点的右子树设置为当前节点右子树的右子树((ROOT)value = 6).rightNode = ROOT.r.r
        6. 当前节点的左子树指向新节点((NODE)value = 4)

        此时
        默认value是根节点
        leftNode是根节点的左子树
         */
        
        Node1 newNode = new Node1(value);
        newNode.leftNode = leftNode;
        newNode.rightNode = rightNode.leftNode;
        value = rightNode.value;
        rightNode = rightNode.rightNode;
        leftNode = newNode;
    }

    /**
     * 右旋转方法
     */
    public void rotateRight() {
        /*
        右旋转实现思路
            1. 创建1个新节点,为当前根节点的值((NEW_NODE)value = 10)
            2. 新节点的右子树((NEW_NODE).r)设置为当前节点的右子树((ROOT).r)
            3. 新节点的左子树((NEW_NODE).l)设置为当前节点左子树的右子树((ROOT).l.r)
            4. 把当前根节点的值((ROOT).value)换成左子树节点的值((ROOT).l.value)
            5. 当前节点的左子树设置为当前节点左子树的左子树
            6. 把当前节点的右子树设置为新节点

            此时根节点的左子树节点已经被销毁了
            因为没有任何引用指向该节点
         */
        Node1 newNode = new Node1(value);
        newNode.rightNode = rightNode;
        newNode.leftNode = leftNode.rightNode;
        value = leftNode.value;
        leftNode = leftNode.leftNode;
        rightNode = newNode;
    }
    
    /**
     * 插入节点的方法
     *
     * @param node 需要插入的节点
     */
    public void insertNode(Node1 node) {
        if (node == null) {
            return;
        }
        /*
        程序运行到此处
        此时可以判断插入的节点和根节点的关系
        this.value表示根节点的值
         */
        if (node.value < this.value) {
            if (this.leftNode == null) {
                /*
                程序运行到此处
                说明当前插入的节点比根节点的值小
                并且根节点的左子树位空
                直接插入即可
                 */
                this.leftNode = node;
            } else {
                this.leftNode.insertNode(node);
            }
        } else {
            /*
            程序运行到此处
            说明当前节点比根节点大
            需要插入到右子树
             */
            if (this.rightNode == null) {
                this.rightNode = node;
            } else {
                this.rightNode.insertNode(node);
            }
        }
        /*
        程序运行到此处
        应该判断左子树和右子树的高度
        如果右子树的高度 - 左子树的高度 > 1
        应该左旋转
        如果左子树的高度 - 右子树的高度 > 1
        应该右旋转
         */
        if (rightTreeHeight() - leftTreeHeight() > 1) {
            /*
            程序运行到此处
            先不考虑那么多
            直接左旋转
             */
            rotateLeft();
        }
        if (leftTreeHeight() - rightTreeHeight() > 1) {
            rotateRight();
        }
    }

实现双旋转

/*
    这里分析几个特殊的情况比如给你1个数组
    {10,11,7,6,8,9}
    构造完二叉排序树
    此时满足右旋转的条件
    但进行右旋转过后
    仍然不是1棵平衡二叉树

    双旋转思路分析和条件
    1. 满足右旋转的条件
    2. 左子树的右子树高度 > 右子树高度
    3. 先对当前这个节点的左节点进行左旋转
    4. 再对当前根节点进行右旋转即可
     */
     /**
     * 插入节点的方法
     *
     * @param node 需要插入的节点
     */
    public void insertNode(Node1 node) {
        if (node == null) {
            return;
        }
        /*
        程序运行到此处
        此时可以判断插入的节点和根节点的关系
        this.value表示根节点的值
         */
        if (node.value < this.value) {
            if (this.leftNode == null) {
                /*
                程序运行到此处
                说明当前插入的节点比根节点的值小
                并且根节点的左子树位空
                直接插入即可
                 */
                this.leftNode = node;
            } else {
                this.leftNode.insertNode(node);
            }
        } else {
            /*
            程序运行到此处
            说明当前节点比根节点大
            需要插入到右子树
             */
            if (this.rightNode == null) {
                this.rightNode = node;
            } else {
                this.rightNode.insertNode(node);
            }
        }
        /*
        程序运行到此处
        应该判断左子树和右子树的高度
        如果右子树的高度 - 左子树的高度 > 1
        应该左旋转
        如果左子树的高度 - 右子树的高度 > 1
        应该右旋转
        注意这里需要判断是否满足双旋转的条件
         */
        if (rightTreeHeight() - leftTreeHeight() > 1) {
            if (rightNode != null && rightNode.leftTreeHeight() > rightNode.rightTreeHeight()) {
                rightNode.rotateRight();
                rotateLeft();
            }else {
                rotateLeft();
            }
            return;
        }

        if (leftTreeHeight() - rightTreeHeight() > 1) {
            if (leftNode != null && leftNode.rightTreeHeight() > leftNode.leftTreeHeight()) {
                leftNode.rotateLeft();
                rotateRight();
            }else {
                rotateRight();
            }
        }
    }

总目录

上一篇:二叉排序树的链式存储结构以及增删查操作


下一篇:线索二叉树