数据结构 AVL树概念以及实现插入的功能(含Java代码实现)

为啥要有avl树

avl树是在二叉搜索树下的一种进阶形式,是为了防止二叉搜索树在极端情况下产生的链表化的场景,从而在二叉搜索树的基础上,加上了某些条件来阻止这种极端情况的产生,但不是保证完全平衡,而是放开了一定的条件,使得这种情况不那么难以满足.(条件:左右子树的高度差的绝对值不大于1) ,我们在发现大于1的时候可以使用左右旋转的方式来调整数的形态,从而保证了查找的时候有近似于O(logN)的性能.

缺点:

当然,有得必有失,这样也带来了一定的损耗:浪费了空间来保存新的变量,每次插入都判断是否满足条件,这样导致了插入的效率变低,这也使得这种二叉树不适合连续多次的插入和修改数据.

如果我们需要持续多次的插入数据,那么也有更进一步的数据结构 --- 红黑树 

AVL树的定义

平衡因子

定义为每个节点的左子树和右子树的高度差(左减去右,右减去左都可以表示)

本文使用右减左表示高度差的定义

例如空树的高度差就是0,这就代表着平衡,

当高度差达到-1,就表示左子树高了,但是还在允许的范围之间,无需调整

当高度差达到1,就代表右子树高了,也在允许的范围之内

当左右子树的高度差超过这个范围(绝对值大于2的时候),那么我们就得需要实现左右旋转来满足这种结构的查找效率.

节点代码结构

static class TreeNode{
        public int val;
        public int bf;//balance factor 平衡因子
        public TreeNode left;//左孩子
        public TreeNode right;//右孩子
        public TreeNode parent;//父节点

        public TreeNode(int val) {
            this.val = val;
        }
    }

实现插入功能

总体思路:

1.按照平衡二叉树的比较方式去寻找到应该插入在的节点

2.插入完了修改一下bf(平衡因子)的值

3.判断是否平衡

3.1 bf = 0直接跳出循环即可

3.2 bf = 1 或 bf = -1就继续向上寻找

3.3 bf = 2考虑左旋右旋修改avl树的结构

以下会包含四种失衡以及解决方案

1.LL类型

我们能很容易发现这种不平衡式因为左子树太深而导致的,此时我们要使用右旋来保证结构的正确.

具体步骤就是

1.将subL作为根节点

2.subLR作为parent的左孩子

3.parent作为subL的右孩子

此时我们发现subL和parent的平衡因子都会变成0

2.RR类型

此时我们明显发现右子树的深度要高于左子树两个节点

此时我们就需要进行左旋来调整整体结构的状态

整体步骤与之前类似

1.将subR提出来当根节点

2.subRL作为parent的右子树

3.parent作为subR的左子树

3.RL类型

这个你可以理解是在右子树深的情况下子树是左子树比较深,而前面的方式是左右子树的子树也是呈现同一趋势的.

执行方式

1.先对右子树进行一次右旋,这时候我们发现总体又一次呈现了RR型状态

左旋即可达成平衡的目标

这里如果我们插入的是节点值是55又会有不一样的平衡值

4.LR类型

执行方式也是一样的,我们对左子树进行左旋,再对整体进行右旋即可

这里我直接给出操作形式,其实这里插入的是25也会有另一种结果,在代码中有所体现,读者可自行画图推导

完整avl树插入代码 

public class AVLTree {
    static class TreeNode{
        public int val;
        public int bf;//balance factor 平衡因子
        public TreeNode left;//左孩子
        public TreeNode right;//右孩子
        public TreeNode parent;//父节点

        public TreeNode(int val) {
            this.val = val;
        }
    }
    public TreeNode root;//根节点
    public boolean insert(int val){
        TreeNode node = new TreeNode(val);
        if(root == null) {
            root = node;
            return true;
        }
        TreeNode cur = root;
        TreeNode parent = null;
        while(cur != null){
            if(val < cur.val){
                parent = cur;
                cur = cur.left;
            }else if(val == cur.val){
                return false;
            }else{
                parent = cur;
                cur = cur.right;
            }
        }
        if(parent.val>val){
            parent.left = node;
        }else{
            parent.right = node;
        }
        node.parent = parent;
        //调节负载因子
        cur = node;
        //负载因子的修改
        while(parent != null){
            //先看cur是parent的左还是右
            //平衡因子为右 - 左
            if(cur == parent.right){
                //右树高,因子++
                parent.bf++;
            }else{
                //左树高
                parent.bf--;
            }
            //检查平衡因子变化完了是多少
            if(parent.bf == 0){
                break;
            }else if(parent.bf == 1 || parent.bf == -1){
                //继续向上判断修改平衡因子
                //因为等于1和-1只代表当前子树平衡
                cur = parent;
                parent = parent.parent;
            }else{
                //2或者-2的情况,考虑左旋右旋
                if(parent.bf == 2){
                    if(cur.bf == 1){
                        rotateLeft(parent);
                    }else{
                        //-1的情况
                        rotateRL(parent);
                    }
                }else{
                    //-2的情况
                    if(cur.bf == 1){
                        rotateLR(parent);

                    }else{
                        //-1的情况
                        rotateRight(parent);
                    }
                }
                break;
            }
        }
        return true;
    }

   //左单旋
    private void rotateLeft(TreeNode parent) {
        TreeNode subR = parent.right;
        TreeNode subRL = subR.left;
        parent.right = subRL;
        subR.left = parent;
        if(subRL != null){
            subRL.parent = parent;
        }
        TreeNode pParent = parent.parent;
        parent.parent = subR;
        if(root == parent){
            root = subR;
            root.parent = null;
        }else{
            if(pParent.left == parent){
                pParent.left = subR;
            }else{
                pParent.right = subR;
            }
            subR.parent = pParent;
        }
        subR.bf = parent.bf = 0;
    }

    //右单旋
    private void rotateRight(TreeNode parent){
        //画图便于理解
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        parent.left = subLR;
        subL.right = parent;
        if(subLR != null){
            subLR.parent = parent;
        }
        //必须先记录
        TreeNode pParent = parent.parent;
        parent.parent = subL;
        //检查当前是否为根节点
        if(parent == root){
             root = subL;
             root.parent = null;
        }else{
            //此刻的parent也不一定是根节点,还是有左右树的
            if(pParent.left == parent){
                pParent.left = subL;
            }else{
                pParent.right = subL;
            }
            subL.parent = pParent;
        }
        subL.bf = 0;
        parent.bf = 0;

    }
    //左右双旋
    private void rotateLR(TreeNode parent){
        TreeNode subL = parent.left;
        TreeNode subLR = subL.right;
        int bf = subLR.bf;

        rotateLeft(parent.left);
        rotateRight(parent);

        if(bf == -1){

            subL.bf = 0;
            subLR.bf = 0;
            parent.bf = 1;
        }else if(bf == 1){

            subL.bf = -1;
            subLR.bf = 0;
            parent.bf = 0;
        }
    }
    //右左双旋
    private void rotateRL(TreeNode parent) {
        TreeNode subR = parent.right;
        TreeNode subRL = subR.left;
        int bf = subRL.bf;
        rotateRight(parent.right);
        rotateLeft(parent);
        if(bf == 1){
            parent.bf = -1;
            subR.bf = 0;
            subRL.bf = 0;
        }else if(bf == -1){
            parent.bf = 0;
            subR.bf = 0;
            subRL.bf = 1;
        }
    }


    //验证当前树
    public void inorder(TreeNode root){
        if(root == null){
            return;
        }
        inorder(root.left);
        System.out.println(root.val+" ");
        inorder(root.right);
    }

    private int height(TreeNode root){
        if(root == null){
            return 0;
        }
        int left = height(root.left);
        int right = height(root.right);
        return Math.max(left+1,right+1);
    }
    public boolean isBalanced(TreeNode root){
        if(root == null){
            return true;
        }
        int left = height(root.left);
        int right = height(root.right);
        //判断平衡因子是否有问题
        if (right-left != root.bf){
            System.out.println("这个节点" + root.val +"平衡因子异常");
            return false;
        }
        return Math.abs(left-right)<=1&&
                isBalanced(root.left) && isBalanced(root.right);
    }

    public static void main(String[] args) {
        int[] arr= new int[]{4, 2, 6, 1, 3, 5, 15, 7, 16,14};
        AVLTree avl = new AVLTree();
        for (int i = 0; i < arr.length; i++) {
            avl.insert(arr[i]);
        }
        System.out.println(avl.isBalanced(avl.root));
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/254041.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【【UART 传输数据实验】】

UART 传输数据实验 通信方式在日常的应用中一般分为串行通信&#xff08;serial communication&#xff09;和并行通信&#xff08;parallel communication&#xff09;。 我们再来了解下串行通信的特点。串行通信是指数据在一条数据线上&#xff0c;一比特接一比特地按顺序传…

Python 爬虫开发完整环境部署,爬虫核心框架安装

Python 爬虫开发完整环境部署 前言&#xff1a; ​ 关于本篇笔记&#xff0c;参考书籍为 《Python 爬虫开发实战3 》 笔记做出来的一方原因是为了自己对 Python 爬虫加深认知&#xff0c;一方面也想为大家解决在爬虫技术区的一些问题&#xff0c;本篇文章所使用的环境为&#x…

Esxi中的AlmaLinux硬盘扩容

Esxi中的AlmaLinux硬盘扩容 通过本文能学习到 虚拟机中的AlmaLinux硬盘扩容 本文主要包括3部分内容&#xff1a; 1. 需要进行扩容的原因 2. 写这篇文章的目的 3. 扩容实操需要进行扩容的原因 近日&#xff0c;使用Jenkins部署时&#xff0c;出现镜像向Nexus私服推送镜像时…

展示一段比较简单的人工智能自动做模型的程序

人工智能是一种模拟或模仿人类智能的技术。它通过使计算机系统具有一定的认知能力和学习能力&#xff0c;使其能够自动完成一系列复杂的任务。人工智能可以在各个领域应用&#xff0c;包括图像识别、语音识别、自然语言处理、机器学习等。人工智能还可以用于解决各种问题&#…

互联网加竞赛 python 机器视觉 车牌识别 - opencv 深度学习 机器学习

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于python 机器视觉 的车牌识别系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;3分 &#x1f9ff; 更多资…

高通平台开发系列讲解(AI篇)SNPE工作流程介绍

文章目录 一、转换网络模型二、量化2.1、选择量化或非量化模型2.2、使用离线TensorFlow或Caffe模型2.3、使用非量化DLC初始化SNPE2.4、使用量化DLC初始化SNPE三、准备输入数据四、运行加载网络沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要介绍SNPE模型工作…

Spring01

一、Spring概述 自 2004年 4 月&#xff0c;Spring 1.0 版本正式发布以来&#xff0c;Spring 已经步入到了第 5 个大版本&#xff0c;也就是我们常说的 Spring 5。 Spring的基础是Spring Framework&#xff0c;其功能有&#xff1a; 1、IoC (控制反转)&#xff0c;Spring 两大…

鸿蒙 Ark ui 实战登录界面请求网络实现教程

团队介绍 作者&#xff1a;徐庆 团队&#xff1a;坚果派 公众号&#xff1a;“大前端之旅” 润开鸿生态技术专家&#xff0c;华为HDE&#xff0c;CSDN博客专家&#xff0c;CSDN超级个体&#xff0c;CSDN特邀嘉宾&#xff0c;InfoQ签约作者&#xff0c;OpenHarmony布道师&…

18个非技术面试题

请你自我介绍一下你自己&#xff1f; 这道面试题是大家在以后面试过程中会常被问到的&#xff0c;那么我们被问到之后&#xff0c;该如果回答呢&#xff1f;是说姓名&#xff1f;年龄&#xff1f;还是其他什么&#xff1f; 最佳回答提示&#xff1a; 一般人回答这个问题往往会…

接口测试--参数实现MD5加密签名规则

最近有个测试接口需求&#xff0c;接口有签名检查&#xff0c;签名规范为将所有请求参数按照key字典排序并连接起来进行md5加密&#xff0c;格式是&#xff1a;md5(bar2&baz3&foo1),得到签名&#xff0c;将签名追加到参数末尾。由于需要对参数进行动态加密并且做压力测…

基于Python实现的一个书法字体风格识别器源码,通过输入图片,识别出图片中的书法字体风格,采用Tkinter实现GUI界面

项目描述 本项目是一个书法字体风格识别器&#xff0c;通过输入图片&#xff0c;识别出图片中的书法字体风格。项目包含以下文件&#xff1a; 0_setting.yaml&#xff1a;配置文件&#xff0c;包含书法字体风格列表、图片调整大小的目标尺寸等设置。1_Xy.py&#xff1a;预处理…

vue3 插槽slot

插槽是子组件中的提供给父组件使用的一个占位符&#xff0c;用 <slot> 表示&#xff0c;父组件可以在这个占位符中填充任何模板代码&#xff0c;如 HTML、组件等&#xff0c;填充的内容会替换子组件的<slot> 元素。<slot> 元素是一个插槽出口 (slot outlet)&…

Web前端-HTML(初识)

文章目录 1.认识WEB1.1 认识网页&#xff0c;网站1.2 思考 2. 浏览器&#xff08;了解&#xff09;2.1 五大浏览器2.2 查看浏览器占有的市场份额 3. Web标准&#xff08;重点&#xff09;3.1 Web 标准构成结构表现行为 1.认识WEB 1.1 认识网页&#xff0c;网站 网页主要由文字…

ADB:获取坐标

命令&#xff1a; adb shell getevent | grep -e "0035" -e "0036" adb shell getevent -l | grep -e "0035" -e "0036" 这一条正确&#xff0c;但是&#xff0c;grep给过滤了&#xff0c;导致没有输出 getevent -c 10 //输出10条信息…

Linux---远程登录、远程拷贝命令

1. 远程登录、远程拷贝命令的介绍 命令说明ssh远程登录scp远程拷贝 2. ssh命令的使用 ssh是专门为远程登录提供的一个安全性协议&#xff0c;常用于远程登录&#xff0c;想要使用ssh服务&#xff0c;需要安装相应的服务端和客户端软件&#xff0c;当软件安装成功以后就可以使…

雷士、书客、松下护眼台灯怎么样?多方位测评对比爆料!

灯光对于我们而言&#xff0c;重要性不言而喻&#xff0c;不管是办公、休闲&#xff0c;还是学习阅读&#xff0c;都离不开它的存在&#xff0c;尤其的在夜晚的时候。所以一般来说都会准备一盏桌面照明的台灯&#xff0c;目前而言最受欢迎的台灯就是护眼台灯了&#xff0c;因为…

企业电子招标采购系统源码Spring Cloud + Spring Boot + 前后端分离 + 二次开发

项目说明 随着公司的快速发展&#xff0c;企业人员和经营规模不断壮大&#xff0c;公司对内部招采管理的提升提出了更高的要求。在企业里建立一个公平、公开、公正的采购环境&#xff0c;最大限度控制采购成本至关重要。符合国家电子招投标法律法规及相关规范&#xff0c;以及审…

免费分享一套Springboot+Vue前后端分离的在线图书商城(书城)系统,挺漂亮的

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的SpringbootVue前后端分离的在线图书商城(书城)系统&#xff0c;分享下哈。 项目视频演示 【免费】SpringbootVue在线图书商城(在线书城) 毕业设计 Java毕业设计_哔哩哔哩_bilibili【免费】SpringbootVue在…

【动态读取配置文件】ParameterTool读取带环境的配置信息

不同环境Flink配置信息是不同的&#xff0c;为了区分不同环境的配置文件&#xff0c;使用ParameterTool工具读取带有环境的配置文件信息 区分环境的配置文件 三个配置文件&#xff1a; flink.properties&#xff1a;决定那个配置文件生效 flink-dev.properties&#xff1a;测…

国产or进口?台阶仪为何要选择国产

在微观轮廓测量领域&#xff0c;选择一款合适的台阶仪对于获得精准的测量结果至关重要。随着科技的不断发展&#xff0c;台阶仪市场上涌现了许多国产和进口产品&#xff0c;消费者在选择时可能会面临一些疑虑。 什么是台阶仪 台阶仪是一种超精密接触式微观轮廓测量仪&#xf…