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

news2024/11/27 6:22:55

为啥要有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));
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Vulnhub 靶场】【IA: Keyring (1.0.1)】【中等】【20210730】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/ia-keyring-101,718/ 靶场下载&#xff1a;https://download.vulnhub.com/ia/keyring-v1.01.ova 靶场难度&#xff1a;中等 发布日期&#xff1a;2021年07月30日 文件大小&#xff1a;1.1 GB 靶场作者&#xf…

Day63力扣打卡

打卡记录 寻找最近的回文数&#xff08;模拟&#xff09; 链接 class Solution:def nearestPalindromic(self, n: str) -> str:m len(n)candidates [10 ** (m - 1) - 1, 10 ** m 1]selfPrefix int(n[:(m 1) // 2])for x in range(selfPrefix - 1, selfPrefix 2):y …

ArcMap自定义脚本工具箱迁移至ArcGIS pro

本文记录了将ArcMap10.7创建的自定义脚本工具箱&#xff08;.tbx&#xff09;迁移至ArcGIS pro的过程 ArcGIS Pro使用的是python版本与ArcMap不同&#xff0c;前者为python3&#xff0c;后者为python2。由于python3 和 python2 的部分语法不兼容&#xff0c;以及一些地理处理工…

性能测试之Artillery(示例及指标)

官方文档&#xff1a;https://www.artillery.io/docs/get-started/first-test PS:文档挺详细&#xff0c;教程比较全 示例 config:http:extendedMetrics: truetarget: http://127.0.0.1:8005phases:- duration: 10 # 持续时间arrivalRate: 10 # 每秒创建10个用户rampTo: 100 …

【漏洞复现】CVE-2023-6848 kodbox远程命令执行

漏洞描述 kodbox 是一个网络文件管理器。它也是一个网页代码编辑器,允许您直接在网页浏览器中开发网站。您可以在基于 Linux、Windows 或 Mac 的平台上在线或本地运行 kodbox。唯一的要求是要有 PHP 5及以上。 kalcaddle kodbox 中发现漏洞,最高版本为 1.48。它已被宣布为关…

LV.13 D5 uboot概述及SD卡启动盘制作 学习笔记

一、uboot概述 1.1 开发板启动过程 开发板上电后首先运行SOC内部iROM中固化的代码(BL0)&#xff0c;这段代码先对基本的软硬件环境(时钟等...)进行初始化&#xff0c;然后再检测拨码开关位置获取启动方式&#xff0c;然后再将对应存储器中的uboot搬移到内存&#xff0c;然后跳…

目标跟踪 MOT数据集和可视化

目录 MOT15数据集格式简介 gt可视化 本人修改的GT可视化代码&#xff1a; MOT15数据集格式简介 以下内容转自&#xff1a;【目标跟踪】MOT数据集GroundTruth可视化-腾讯云开发者社区-腾讯云 MOT15数据集下载&#xff1a;https://pan.baidu.com/s/1foGrBXvsanW8BI4eybqfWg?…

机器学习算法---时间序列

类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统计学检验箱…

vue3中ref及reactive的说明

目录 1.响应式说明 2.vue3的ref及reactive的使用 3.reactive响应式失效问题 4.总结 1.响应式说明 vue的响应式是vue框架中的核心概念之一&#xff0c;它是指当数据发生变化时&#xff0c;vue能够自动更新视图。vue2的响应式是基于Object.defineProperty进行实现的。 当你把…

安全密码(字符串)

#include <stdio.h> #include <stdbool.h> #include <string.h> bool is_secure_password(const char* password); int main() {int M;char password[51];// 读取输入中的密码数量 Mscanf("%d", &M);// 处理每个密码for (int i 0; i < M; …

智慧校园2.0物联网管理平台建设方案:PPT全文22页,附下载

关键词&#xff1a;物联网解决方案&#xff0c;智慧校园解决方案&#xff0c;物联网平台建设方案&#xff0c;物联网应用技术 一、智慧校园2.0物联网管理平台建设背景 1、教育现代化和强国建设的需要&#xff1a;近年来&#xff0c;国家为了加快推进教育现代化、教育强国建设…

OpenSergo Dubbo 微服务治理最佳实践

*作者&#xff1a;何家欢&#xff0c;阿里云 MSE 研发工程师 Why 微服务治理&#xff1f; 现代的微服务架构里&#xff0c;我们通过将系统分解成一系列的服务并通过远程过程调用联接在一起&#xff0c;在带来一些优势的同时也为我们带来了一些挑战。 如上图所示&#xff0c;可…

Eclipse 自动生成注解,如果是IDEA可以参考编译器自带模版进行修改

IDEA添加自动注解 左上角选择 File -> Settings -> Editor -> File and Code Templates&#xff1b; 1、添加class文件自动注解&#xff1a; ​/*** <b>Function: </b> todo* program: ${NAME}* Package: ${PACKAGE_NAME}* author: Jerry* date: ${YEA…

Apache Flume(5):多个agent模型

可以将多个Flume agent 程序连接在一起&#xff0c;其中一个agent的sink将数据发送到另一个agent的source。Avro文件格式是使用Flume通过网络发送数据的标准方法。 从多个Web服务器收集日志&#xff0c;发送到一个或多个集中处理的agent&#xff0c;之后再发往日志存储中心&…

Maui blazor与sqlite开发一个增删改查

在android端增删改不能运行。也看不出来是什么&#xff0c;但运行到windows可以运行。 引入sqlite-net-pcl 开发Model using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.T…

深入探讨线程池及其关键参数

目录 引言 1. 线程池概述 2. 线程池的工作原理 3. 线程池的关键参数 4. 线程池的最佳实践 5. 实际应用场景 结论 引言 在并发编程领域&#xff0c;线程池是一种重要的工具&#xff0c;用于管理和重用线程&#xff0c;提高程序的性能和效率。线程池可以有效地管理线程的生…

VMware----基于 VMware 玩转 CentOS 虚拟机创建、克隆以及配置后台运行

查看原文 文章目录 一、安装 Vmware二、创建 CentOS7 系统的虚拟机三、克隆虚拟机四、设置虚拟机后台运行 一、安装 Vmware &#xff08;1&#xff09;打开VMware下载地址页面&#xff0c;滑动页面&#xff0c;找到如下界面&#xff0c;点击【下载】 &#xff08;2&#xff…

计算机服务器中了mkp勒索病毒怎么办,mkp勒索病毒解密恢复

在计算机技术飞速发展的今天&#xff0c;越来越多的企业走向了数字化办公模式&#xff0c;极大地方便了企业的生产运营&#xff0c;为企业带来了更高的效率。但网络威胁无处不在&#xff0c;网络威胁手段随着计算机技术的不断发展也在不断增加。近期&#xff0c;云天数据恢复中…

数据仓库与数据挖掘c5-c7基础知识

chapter5 分类 内容 分类的基本概念 分类 数据对象 元组(x,y) X 属性集合 Y 类标签 任务 基于有标签的数据&#xff0c;学习一个分类模型&#xff0c;通过这个分类模型&#xff0c;可以把一组属性x映射到一个特定的类别y上 类别y 提前设定好的--如&#xff1a;学生…

【CMU 15-445】Lecture 12: Query Execution I 学习笔记

Query Execution I Processing ModelsIterator ModelMaterialization ModelVectorization Model Access MethodsSequential ScanIndex Scan Modification QueriesHalloween Problem 本节课主要介绍SQL语句执行的相关机制。 Processing Models 首先是处理模型&#xff0c;它定义…