• 0091 平衡二叉树


     

     

     

     

     

    /*
     * 平衡二叉树
     * 1.也叫平衡二叉搜索树,也成为AVL树,可以保证查询效率较高
     * 2.特点:是一颗空树或左右子树的高度差的绝对值不超过1,且左右两个子树都是一颗平衡二叉树
     *   常见实现方法有:红黑树,AVL,替罪羊树,Treap,伸展树等
     * 
     * 应用——单旋转(左旋转)
     * 给定数列{4,3,6,5,7,8},创建出对应的平衡二叉树
     * 左旋转
     * 1.创建一个新结点newNode,值等于当前根节点的值(4)
     * 2.把新结点的左子树设置成当前节点的左子树 
     *         newNode.left = left
     * 3.把新结点的右子树设置为当前结点的右子树的左子树
     *         newNode.right = right.left
     * 4.把当前结点的值换为右子节点的值
     *         value = right.value
     * 5.把当前结点的右子树设为右子树的右子树
     *         right = right.right
     * 6.把当前结点的左子树设为新结点
     *         left = newNode
     * 
     * 应用——单旋转(右旋转)
     * 给定数列{10,12,8,9,7,6},创建出对应的平衡二叉树
     * 右旋转
     * 1.创建一个新结点newNode,值等于当前根节点的值(10)
     * 2.把新结点的右子树设置成当前节点的右子树 
     *         newNode.right = right
     * 3.把新结点的左子树设置为当前结点的左子树的右子树
     *         newNode.left = left.right
     * 4.把当前结点的值换为左子节点的值
     *         value = left.value
     * 5.把当前结点的左子树设为左子树的左子树
     *         left = left.left
     * 6.把当前结点的右子树设为新结点
     *         right = newNode
     * 
     * 应用——双旋转
     * 1.满足左(右)旋转条件,但左(右)旋转后仍不是平衡二叉树
     * 2.如果右(左)子树的左(右)子树高度大于右(左)子树的高度
     *   先对当前结点的右(左)子节点进行左旋转
     *   再对当前结点进行左(右)旋转
     */
    public class AVL_ {
        public static void main(String[] args) {
            //int[] arr = {4,3,6,5,7,8};
            //int[] arr = {10,12,8,9,7,6};
            int[] arr = {10,11,7,6,8,9};
            //创建一个AVL对象
            AVL avl = new AVL();
            //添加节点
            for(int i = 0;i < arr.length;i++) {
                avl.add(new Node(arr[i]));
            }
            
            //中序遍历
            avl.infixOrder();
            
            //树的高度
            System.out.println("树的高度=" + avl.getRoot().height());
            System.out.println("左子树的高度=" + avl.getRoot().leftHeight());
            System.out.println("右子树的高度=" + avl.getRoot().rightHeight());
            System.out.println("当前根节点=" + avl.getRoot());
        }
    }

    //创建AVL树
    class AVL{
        private Node root;
        
        public Node getRoot() {
            return root;
        }
        
        //添加节点的方法
        public void add(Node node) {
            if (root == null) {
                root = node;
            }else {
                root.add(node);
            }
        }
        
        //中序遍历
        public void infixOrder() {
            if (root == null) {
                System.out.println("树空,无法遍历");
            }else {
                root.infixOrder();
            }
        }
    }

    //创建Node
    class Node{
        int value;
        Node left;
        Node right;
        public Node(int value) {
            this.value = value;
        }
        
        //返回左子树高度
        public int leftHeight() {
            if (left == null) {
                return 0;
            }
            return left.height();
        }
        
        //返回右子树高度
        public int rightHeight() {
            if (right == null) {
                return 0;
            }
            return right.height();
        }
        
        //返回以该结点为根节点的树的高度
        public int height() {
            return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
        }
        
        //左旋转方法
        private void leftRotate() {
            //1.创建一个新结点newNode,值等于当前根节点的值(4)
            Node newNode = new Node(value);
            //2.把新结点的左子树设置成当前节点的左子树 
            newNode.left = left;
            //3.把新结点的右子树设置为当前结点的右子树的左子树
            newNode.right = right.left;
            //4.把当前结点的值换为右子节点的值
            value = right.value;
            //5.把当前结点的右子树设为右子树的右子树
            right = right.right;
            //6.把当前结点的左子树设为新结点
            left = newNode;
        }
        
        //右旋转方法
        private void rightRotate() {
            //1.创建一个新结点newNode,值等于当前根节点的值(10)
            Node newNode = new Node(value);
            //2.把新结点的右子树设置成当前节点的右子树 
            newNode.right = right;
            //3.把新结点的左子树设置为当前结点的左子树的右子树
            newNode.left = left.right;
            //4.把当前结点的值换为左子节点的值
            value = left.value;
            //5.把当前结点的左子树设为左子树的左子树
            left = left.left;
            //6.把当前结点的右子树设为新结点
            right = newNode;
        }
        
        @Override
        public String toString() {
            return "Node [value=" + value + "]";
        }

        //添加
        //递归形式添加节点,满足二叉排序树要求
        public void add(Node node) {
            if (node == null) {
                return;
            }
            
            //判断传入节点的值,和当前子树的根节点的值的关系
            //添加节点值小于当前节点值
            if (node.value < this.value) {
                //如果当前左子节点为null
                if (this.left == null) {
                    this.left = node;
                }else {
                    //向左递归
                    this.left.add(node);
                }
            //添加节点值大于当前节点值
            }else {
                if (this.right == null) {
                    this.right = node;
                }else {
                    //向右递归
                    this.right.add(node);
                }
            }
            
            //当添加完一个结点后,如果 (右子树高度 - 左子树高度) > 1,左旋转
            if (rightHeight() - leftHeight() > 1) {
                //如果右子树的左子树高度大于右子树的高度
                if (right != null && right.leftHeight() > right.rightHeight()) {
                    //先对当前结点的右子节点进行右旋转
                    right.rightHeight();
                    //再对当前结点进行左旋转
                    leftRotate();
                }else {
                    leftRotate();//左旋转
                }
                return;
            }
            
            //当添加完一个结点后,如果 (左子树高度 - 右子树高度) > 1,右旋转
            if (leftHeight() - rightHeight() > 1) {
                //如果左子树的右子树高度大于左子树的高度
                if (left != null && left.rightHeight() > left.leftHeight()) {
                    //先对当前结点的左子节点进行左旋转
                    left.leftRotate();
                    //再对当前结点进行右旋转
                    rightRotate();
                }else {
                    rightRotate();//右旋转
                }
            }
        }
        
        //中序遍历
        public void infixOrder() {
            if (this.left != null) {
                this.left.infixOrder();
            }
            System.out.println(this);
            if (this.right != null) {
                this.right.infixOrder();
            }
        }
    }

    /*
     * 多叉树(了解)
     * 1.在二叉树中,每个结点有数据项,最多有两个子节点
     *   如果允许每个结点可以有更多的数据项和更多的子节点,就是多叉树
     * 2.如2-3树,2-3-4树....多叉树通过重新组织结点,减少树的高度,对二叉树进行了优化
     * 
     * B树
     * B树通过重新组织结点,减少树的高度,且减少I/O读写次数来提升效率
     * 1.文件系统及数据库系统的设计者利用了磁盘预读原理,将一个结点的大小设为等于一个页
     *   (页的大小通常为4k),这样每个结点只需一次I/O就可以完全载入
     * 2.将树的度M设置为1024,在600亿个元素中最多只需4次I/O操作就可以读取想要的元素
     *   B树广泛应用于文件存储系统以及数据库系统中
     *   
     * 2-3树
     * 是最简单的B树结构,具有以下特点:
     * 1.2-3树的所有叶子节点都在同一层(只要是B树都满足这个条件)
     * 2.有两个子节点的节点叫二节点,二节点要么没有子节点,要么有两个子节点
     * 3.有三个子节点的节点叫三节点,三节点要么没有子节点,要么有三个子节点
     * 4.2-3树是由二节点和三节点构成的树
     * 
     * 2-3-4树:概念和2-3类似,由二节点,三节点和四节点构成
     * 
     * B树
     * 1.B树的阶:节点的最多子节点个数。如2-3树的阶是3,2-3-4树的阶是4
     * 2.B树的搜索:从根节点开始,对节点内的关键字(有序)序列进行二分查找
     *      如果命中则结束,否则进入查询关键字所属范围的子节点,重复,
     *   直到所对应的子指针为空,或已是叶子节点
     * 3.关键字集合分布在整棵树中,即叶子节点和非叶子节点都存放数据
     * 4.搜索有可能在非叶子节点结束
     * 5.其搜索性能等价于在关键字全集内做一次二分查找
     * 
     * B+树
     * B+树是B树的变体,也是一种多路搜索树
     * 1.B+树的搜索与B树基本相同,区别是B+树只要达到叶子节点才命中
     *      其性能也等价于在关键字全集做一次二分查找
     * 2.所有关键字都出现在叶子节点的链表中(即数据只能在叶子节点)
     *   且链表中的关键字恰好是有序的
     * 3.非叶子节点相当于是叶子节点的索引,叶子节点相当于是存储数据的数据层
     * 4.适用于文件索引系统
     * 
     * B*树
     * B*树是B+树的变体,在B+树的非根和非叶子节点再增加指向兄弟的指针
     * 1.B*树定义了非叶子节点关键字个数至少为(2/3)*M,即块的最低使用率为2/3
     *   而B+树块的最低使用率为1/2
     * 2.B*树分配节点的概率比B+树要低,空间使用率更高
     * 
     */

     

     

     

  • 相关阅读:
    ✨ StarRocks 10 月社区动态
    BetaFlight模块设计之三十五:RSSI信号强度&链路稳定性分析
    Profinet转RS485Modbus网关M2AC系列伺服驱动器配置方法
    【附源码】计算机毕业设计JAVA忆居民宿管理
    java反射与注解详解,共同实现动态代理模式
    C++ 基础(十二)函数-题目练习
    mac电脑卸载LVSecurityAgent
    软考 系统架构设计师系列知识点之数字孪生体(4)
    最长不下降子序列(接上一篇)
    Git配置SSH
  • 原文地址:https://blog.csdn.net/m0_72797089/article/details/127811435