Java实现AVL树

news2025/1/10 16:52:49

AVI树

如果一颗二叉搜索树不平衡,那么搜索效率会受影响

二叉搜索树如果不是这种不平衡的情况,时间复杂度可以达到O(logn) 但是像图中的这种不平衡情况时间复杂度为O(n),那么如何解决呢? 可以通过旋转解决

旋转之后并不会破坏二叉搜索树的特性

判断是否平衡有一个规则:如果一个节点的左右孩子,高度差超过1,则此节点失衡,才需要旋转

这就是引出这样一棵树:自平衡二叉搜索树

ta的实现有很多种,但其中一种比较著名的实现叫做AVL,为什么叫AVL呢?是因为有两位作者一位取了名字中的AV,另外一个取了L,所以叫AVL树

实现:

/**
 * <h3>AVL树</h3>
 *
 * <ul>
 *     <li>二叉搜索树在插入和删除时,节点可能失衡</li>
 *     <li>如果在插入和删除时通过旋转,始终让二叉搜索树保持平衡,称为自平衡的二叉搜索树</li>
 *     <li>AVL是自平衡二叉树的实现之一</li>
 * </ul>
 */
public class AVLTree {

    static class AVLNode{
        int key;
        Object value;
        AVLNode left;
        AVLNode right;
        int height=1;//高度

        public AVLNode(int key, Object value) {
            this.key = key;
            this.value = value;
        }

        public AVLNode(int key) {
            this.key = key;
        }

        public AVLNode(int key, Object value, AVLNode left, AVLNode right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }
    // 求节点高度
    private int height(AVLNode node){
        return node==null?0:node.height;
    }

    // 更新节点高度(新增,删除,旋转)
    private void updateHeight(AVLNode node){
        node.height = Integer.max(height(node.left),height(node.right))+1;
    }

    // 平衡因子(balance factor) = 左子树高度 - 右子树高度
    private int bf(AVLNode node){
        return height(node.left) - height(node.right);
    }
    //0 -1 1 平衡的
    //> 1 <-1不平衡  bf > 1时,表示左边太高   bf<-1时,表示右边太高

}

求节点高度的时候可以参照前面写过的求二叉树最大深度的题目

AVI树的旋转 

第一种需要旋转的: LL类型 

 

第二种情况:LR类型 - 

另外两种情况就RR  RL 跟上面两种对称

总的来讲:

旋转:对于LL就是一次右旋;RR就是一次左旋;LR就是对左子树一次左旋;失衡节点右旋;RL就是对右子树进行一次右旋,失衡节点进行一次左旋;

虽然分了四种情况但是无非也就是左旋和右旋

 

 // 参数:要旋转的点,返回值:新的根节点
    private AVLNode rightRotate(AVLNode node){//这里的node相当于red
        AVLNode left = node.left;//left相当于yellow

        left.right = node;
        return left;
    }

 相当于:

  // 参数:要旋转的点,返回值:新的根节点
    private AVLNode rightRotate(AVLNode red){
        AVLNode yellow = red.left;
        
        yellow.right = red;
        return yellow;
    }

那来看一种稍微复杂一点的情况:

上面的例子中没有考虑黄色节点有右孩子(绿色节点)的情况

 // 参数:要旋转的点,返回值:新的根节点
    private AVLNode rightRotate(AVLNode red){
        AVLNode yellow = red.left;
        AVLNode green = yellow.right;
        yellow.right = red; // 上位
        red.left=green; // 换爹  
        return yellow;
    }

左旋情况:

  // 参数:要旋转的点,返回值:新的根节点
    private AVLNode leftRotate(AVLNode red){
        AVLNode yellow = red.right;
        AVLNode green = yellow.left;
        yellow.left = red;
        red.right = green;
        return yellow;
    }


    // 先左旋左子树,再右旋根节点 LR
    private AVLNode LeftRightRotate(AVLNode node){
        node.left = leftRotate(node.left);//左旋完成了
        return rightRotate(node);
    }
    // 先右旋右子树,再左旋根节点
    private AVLNode RightLeftRotate(AVLNode node){
        node.right = rightRotate(node.right);
        return leftRotate(node);
    }

旋转后节点需要更新

 // 参数:要旋转的点,返回值:新的根节点
    private AVLNode rightRotate(AVLNode red){
        AVLNode yellow = red.left;
        AVLNode green = yellow.right;
        yellow.right = red; // 上位
        red.left=green; // 换爹
        updateHeight(red);//不带颜色的节点颜色都不会变 和 绿色也不会变
        updateHeight(yellow);
        return yellow;
    }
    // 参数:要旋转的点,返回值:新的根节点
    private AVLNode leftRotate(AVLNode red){
        AVLNode yellow = red.right;
        AVLNode green = yellow.left;
        yellow.left = red;
        red.right = green;
        updateHeight(red);//不带颜色的节点颜色都不会变 和 绿色也不会变
        updateHeight(yellow);//这两行不能调位置
        return yellow;
    }

基于以上的实现,我们可以写一个方法检查节点是否失衡,重新平衡代码

 // 检查节点是否失衡,重新平衡代码
    private AVLNode balance(AVLNode node){
        if(node==null){
            return null;
        }
        int bf = bf(node);
        if(bf>1 && bf(node.left)>0){//LL
            return rightRotate(node);
        }else if(bf>1&&bf(node.left)<0){//LR
            return LeftRightRotate(node);
        }else if(bf<-1 && bf(node.right)>0){//RL
            return RightLeftRotate(node);
        }else if(bf<-1&&bf(node.right)>0){//RR
            return leftRotate(node);
        }
        return node;

    }

但是有两种情况无法处理:发生在删除中

但其实一次右旋即可 所以应该是

最终代码:

// 检查节点是否失衡,重新平衡代码
    private AVLNode balance(AVLNode node){
        if(node==null){
            return null;
        }
        int bf = bf(node);
        if(bf>1 && bf(node.left)>=0){//LL
            return rightRotate(node);
        }else if(bf>1&&bf(node.left)<0){//LR
            return LeftRightRotate(node);
        }else if(bf<-1 && bf(node.right)>0){//RL
            return RightLeftRotate(node);
        }else if(bf<-1&&bf(node.right)<=0){//RR
            return leftRotate(node);
        }
        return node;

    }

新增

 AVLNode root;

    public void put(int key,Object value){
        root = doPut(root,key,value);
    }


    private AVLNode doPut(AVLNode node,int key, Object value){
        // 1.找到空位,创建新节点
        if(node==null){
            return new AVLNode(key,value);//刚创建高度为1
        }
        // 2.key 已存在, 更新
        if(key == node.key){
            node.value = value;
            return node;
        }
        // 3.继续查找
        if(key<node.key){
            node.left = doPut(node.left,key,value);// 向左
        }else{
            node.right = doPut(node.right,key,value);// 向右
        }
        updateHeight(node);
        return balance(node);
    }

删除

 

  public void remove(int key){
        root = doRemove(root,key);
    }
    private AVLNode doRemove(AVLNode node,int key){
        //1.node == null
        if(node==null){
            return null;
        }
        //2.没找到key
        if(node.key>key){
            node.left = doRemove(node.left,key);
        }else if(node.key<key){
            node.right = doRemove(node.right,key);
        }
        //3.找到key   1) 没有  2) 只有一个孩子  3) 有两个孩子
        else{
            if(node.left==null&&node.right==null){
                return null;//删剩下的
            }else if(node.left==null){
                node = node.right;
            }else if(node.right==null){
                node = node.left;
            }else{
                //找后继
                AVLNode s = node.left;
                while(s.left!=null){
                    s= s.left;
                }
                //s:后继节点
                //处理后继节点后事
                s.right = doRemove(node.right,s.key);
                s.left =node.left;
                node = s;
            }
        }
        //4.更新高度
        updateHeight(node);
        //5.检查失衡balance
        return balance(node);
    }

AVI树完整代码

/**
 * <h3>AVL树</h3>
 *
 * <ul>
 *     <li>二叉搜索树在插入和删除时,节点可能失衡</li>
 *     <li>如果在插入和删除时通过旋转,始终让二叉搜索树保持平衡,称为自平衡的二叉搜索树</li>
 *     <li>AVL是自平衡二叉树的实现之一</li>
 * </ul>
 */
public class AVLTree {

    static class AVLNode{
        int key;
        Object value;
        AVLNode left;
        AVLNode right;
        int height=1;//高度

        public AVLNode(int key, Object value) {
            this.key = key;
            this.value = value;
        }

        public AVLNode(int key) {
            this.key = key;
        }

        public AVLNode(int key, Object value, AVLNode left, AVLNode right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }
    // 求节点高度
    private int height(AVLNode node){
        return node==null?0:node.height;
    }

    // 更新节点高度(新增,删除,旋转)
    private void updateHeight(AVLNode node){
        node.height = Integer.max(height(node.left),height(node.right))+1;
    }

    // 平衡因子(balance factor) = 左子树高度 - 右子树高度
    private int bf(AVLNode node){
        return height(node.left) - height(node.right);
    }
    //0 -1 1 平衡的
    //> 1 <-1不平衡  bf > 1时,表示左边太高   bf<-1时,表示右边太高
/*
LL
    - 失衡节点的bf>1,即左边更高
    - 失衡节点的左孩子的bf>=0,即做孩子这边也是左边更高或等高

LR
    - 失衡节点的bf>1,即左边更高
    - 失衡节点的做孩子的bf<0,即做孩子这边是右边更高

RL
    - 失衡节点的bf<-1,即右边更高
    - 失衡接地那的右孩子的bf>0,即右孩子这边左边更高

RR
    - 失衡节点的bf<-1,即右边更高
    - 失衡节点的右孩子的bf<=0,即右孩子这边右边更高或等高
*/

    // 参数:要旋转的点,返回值:新的根节点
    private AVLNode rightRotate(AVLNode red){
        AVLNode yellow = red.left;
        AVLNode green = yellow.right;
        yellow.right = red; // 上位
        red.left=green; // 换爹
        updateHeight(red);//不带颜色的节点颜色都不会变 和 绿色也不会变
        updateHeight(yellow);
        return yellow;
    }
    // 参数:要旋转的点,返回值:新的根节点
    private AVLNode leftRotate(AVLNode red){
        AVLNode yellow = red.right;
        AVLNode green = yellow.left;
        yellow.left = red;
        red.right = green;
        updateHeight(red);//不带颜色的节点颜色都不会变 和 绿色也不会变
        updateHeight(yellow);//这两行不能调位置
        return yellow;
    }

    // 先左旋左子树,再右旋根节点 LR
    private AVLNode LeftRightRotate(AVLNode node){
        node.left = leftRotate(node.left);//左旋完成了
        return rightRotate(node);
    }
    // 先右旋右子树,再左旋根节点
    private AVLNode RightLeftRotate(AVLNode node){
        node.right = rightRotate(node.right);
        return leftRotate(node);
    }

    // 检查节点是否失衡,重新平衡代码
    private AVLNode balance(AVLNode node){
        if(node==null){
            return null;
        }
        int bf = bf(node);
        if(bf>1 && bf(node.left)>=0){//LL
            return rightRotate(node);
        }else if(bf>1&&bf(node.left)<0){//LR
            return LeftRightRotate(node);
        }else if(bf<-1 && bf(node.right)>0){//RL
            return RightLeftRotate(node);
        }else if(bf<-1&&bf(node.right)<=0){//RR
            return leftRotate(node);
        }
        return node;

    }
    AVLNode root;

    public void put(int key,Object value){
        root = doPut(root,key,value);
    }


    private AVLNode doPut(AVLNode node,int key, Object value){
        // 1.找到空位,创建新节点
        if(node==null){
            return new AVLNode(key,value);//刚创建高度为1
        }
        // 2.key 已存在, 更新
        if(key == node.key){
            node.value = value;
            return node;
        }
        // 3.继续查找
        if(key<node.key){
            node.left = doPut(node.left,key,value);// 向左
        }else{
            node.right = doPut(node.right,key,value);// 向右
        }
        updateHeight(node);
        return balance(node);
    }

    public void remove(int key){
        root = doRemove(root,key);
    }
    private AVLNode doRemove(AVLNode node,int key){
        //1.node == null
        if(node==null){
            return null;
        }
        //2.没找到key
        if(node.key>key){
            node.left = doRemove(node.left,key);
        }else if(node.key<key){
            node.right = doRemove(node.right,key);
        }
        //3.找到key   1) 没有  2) 只有一个孩子  3) 有两个孩子
        else{
            if(node.left==null&&node.right==null){
                return null;//删剩下的
            }else if(node.left==null){
                node = node.right;
            }else if(node.right==null){
                node = node.left;
            }else{
                //找后继
                AVLNode s = node.left;
                while(s.left!=null){
                    s= s.left;
                }
                //s:后继节点
                //处理后继节点后事
                s.right = doRemove(node.right,s.key);
                s.left =node.left;
                node = s;
            }
        }
        //4.更新高度
        updateHeight(node);
        //5.检查失衡balance
        return balance(node);
    }

}

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

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

相关文章

如何进行景气分析

景气分析是一种短期经济分析方法。主要分析短时间内&#xff08;一般指一年内&#xff0c; 或几个月内&#xff09;经济运行的态势&#xff0c;包括当前的状态和未来的趋势。景气分析可以为宏观经济政策提供重要的决策与参考信息&#xff0c;例如根据经济运行的方向、强弱可建议…

【AI开发:音频】二、GPT-SoVITS使用方法和过程中出现的问题(GPU版)

1.FileNotFoundError: [Errno 2] No such file or directory: logs/guanshenxxx/2-name2text-0.txt 这个问题中包含了两个&#xff1a; 第一个&#xff1a;No module named pyopenjtalk 我的电脑出现的就是这个 解决&#xff1a;pip install pyopenjtalk 第二个&#xff1a…

数据结构练习-数据结构概述

----------------------------------------------------------------------------------------------------------------------------- 1. 在数据结构中&#xff0c;从逻辑上可以把数据结构分成( )。 A. 动态结构和静态结构 B. 紧凑结构和非紧凑结构 C. 线性结…

初识ansible变量及实例配置

目录 1、为什么要使用变量 2、变量分类 3、 变量详解 3.1 vars,vars_files , group_vars 3.1 .1 vars 剧本中定义变量 3.1.2 vars_file 将变量存放到一个文件中&#xff0c;并在剧本中引用 3.1.3 group_vars 创建一个变量文件给某个组使用 实例1-根据不同的主机…

CGLIB动态代理

文章目录 前言概要SpringBoot中使用小结 前言 当我们需要在Java中实现动态代理时&#xff0c;通常会考虑使用 JDK原生动态代理 或者 CGLIB动态代理。 我这里说一下CGLIB动态代理&#xff0c;并给出一个例子。 概要 CGLIB&#xff08;Code Generation Library&#xff09;是一…

无损以太网的ROCE革命,队列的缓存空间优化分析

ROCE无损以太网&#xff0c;队列的缓存空间优化 多级缓存架构优化芯片性能&#xff1a;* 缓存空间细分为芯片级、端口级和队列级&#xff0c;实现精细管理。* 无损队列引入Headroom缓存空间&#xff0c;确保数据完整性。 在芯片层面&#xff1a; 静态缓存为端口提供保证的缓存空…

RHCE:网络服务综合项目

基础配置&#xff1a; 1.配置主机名&#xff0c;静态IP地址 2.开启防火墙并配置 3.部分开启SElinux并配置 4.服务器之间使用同ntp.aliyun.com进行时间同步 5.服务器之间实现SSH免密登录 业务需求&#xff1a; 1.Server-NFS-DNS主机配置NFS服务器&#xff0c;将博客网…

智慧园区引领未来产业趋势:科技创新驱动园区发展,构建智慧化产业新体系

目录 一、引言 二、智慧园区引领未来产业趋势 1、产业集聚与协同发展 2、智能化生产与服务 3、绿色可持续发展 三、科技创新驱动园区发展 1、创新资源的集聚与整合 2、创新成果的转化与应用 3、创新文化的培育与弘扬 四、构建智慧化产业新体系 1、优化产业布局与结构…

5.SpringBoot 配置文件

文章目录 1.配置文件作用2.配置文件格式2.1项目中同时存在两种配置文件2.2application.properties2.2.1 application.properties语法格式2.2.2获取自定义配置项 2.3 application.yml2.3.1 application.yml语法格式2.3.1.1单双引号区别2.3.1.2和application.properties格式对比&…

安全狗云眼的主要功能有哪些?

"安全狗云眼"是一款综合性的网络安全产品&#xff0c;主要用于实时监控和保护企业的网络安全。其核心功能包括威胁检测、漏洞扫描、日志管理和合规性检查等。 以下是安全狗云眼的主要功能详细介绍&#xff1a; 1、资产管理 定期获取并记录主机上的Web站点、Web容器、…

Python连接Oracle数据库问题解决及Linux服务器操作知识

背景说明 最近在做一个视频分析的项目&#xff0c;然后需要将视频分析的数据写入到oracle数据库&#xff0c;直接在服务器上测试数据库连接的时候出现了这个bug提示&#xff0c;自己通过不断的研究探讨&#xff0c;最终把这个问题成功进行了解决&#xff0c;在这里进行一下记录…

【周总结】总结下这周的工作、(hashmap)知识巩固等

总结 这周开发任务已经全部结束&#xff0c;主要是在修改一些 jira 问题 需要反思的是&#xff0c;中间改造接口时&#xff0c;数据库表需要新增一个字段&#xff0c;这个 sql 脚本忘记加到 basetable.sql 脚本里面了&#xff0c;这样如果是新建的项目&#xff0c;创建的时候不…

HackMyVM-Hommie

目录 信息收集 arp nmap WEB web信息收集 dirsearch ftp tftp ssh连接 提权 系统信息收集 ssh提权 信息收集 arp ┌──(root㉿0x00)-[~/HackMyVM] └─# arp-scan -l Interface: eth0, type: EN10MB, MAC: 08:00:27:77:ed:84, IPv4: 192.168.9.126 Starting arp-…

常见的经典目标检测算法

目标检测是计算机视觉领域的一个核心任务&#xff0c;它涉及到识别图像中的物体并确定它们的位置。以下是一些常见的经典目标检测算法&#xff1a; R-CNN系列 R-CNN&#xff08;Region-based Convolutional Neural Network&#xff09;是一种用于目标检测的算法&#xff0c;它…

未知个数一维数组:申请资源、初始化、访问、释放资源

示例&#xff1a; /*** brief how about array-unknown-count? show you here.* author wenxuanpei* email 15873152445163.com(query for any question here)*/ #define _CRT_SECURE_NO_WARNINGS//support c-library in Microsoft-Visual-Studio #include <stdio.h>…

Git | Git基本命令

Git | Git基本操作 文章目录 Git | Git基本操作一、创建Git本地仓库1、创建Git仓库2、配置Git3、理解工作区、暂存区、版本库关系 二、添加、修改与查看添加文件查看历史提交记录 修改文件查看.git文件 三、版本回退版本回退撤销修改尚未add已add但还未commit已add并commit 删除…

项目小游戏-贪吃蛇

目录 1.游戏开始 - GameStart 1.1cmd命令窗口 调节窗口命令 ​编辑更改窗口命名 ​编辑 1.2 Win32 API win32 API 的介绍: ​编辑 获取控制台坐标COORD 获取控制台句柄: 获取缓冲台光标信息: 获取虚拟键位: 本地初始化 setlocale(); 游戏开始的具体实现&#xff1a…

uniapp中scroll-view初始化的时候 无法横向滚动到某个为止

项目需求 实现日历&#xff08;13天&#xff09;默认高亮第六天 并定位到第六 左边右边各六天&#xff08;可以滑动&#xff09; 直接上代码 <template><scroll-view class"scroll-X":show-scrollbar"true" :scroll-x"scrollable":…

理解这几个安全漏洞,你也能做安全测试!

如今安全问题显得越来越重要&#xff0c;一个大型的互联网站点&#xff0c;你如果每天查看日志&#xff0c;会发现有很多尝试攻击性的脚本。 如果没有&#xff0c;证明网站影响力还不够大。信息一体化的背后深藏着各类安全隐患&#xff0c;例如由于开发人员的不严谨导致为Web应…

八字入门书介绍

《千里命稿》&#xff0c;此书是民国时期上海的韦千里先生所著&#xff0c;是他主讲的命学培训班的讲义&#xff0c;成书时间在1935年。该书语言简练、述理清晰&#xff0c;是讲述子平命学概念的佼佼者&#xff0c;尤其是对五行、六神的性质讲解&#xff0c;可以说是此书的精华…