Java 实现 AVL树

news2024/9/19 9:45:23

在二叉平衡树中,我们进行插入和删除操作时都需要遍历树,可见树的结构是很影响操作效率的。在最坏的情况下,树成了一个单支树,查找的时间复杂度成了O(N),建树跟没建树一样。那么是不是有什么办法可以建一个树避免这种情况?

一.概念

AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis,其又叫高度平衡树。进行插入和删除操作后对树进行一次或多次旋转,保证每个结点的左右子树高度之差的绝对值不超过1,以达到高度平衡的目的。

1.AVL树本质上还是二叉平衡树,这是必须保证的一点;

2.AVL树在二叉平衡树的基础上加入了一个平衡的条件,即:每个结点的左右子树高度之差的绝对值不超过1。

二叉平衡树:Java Map和Set-CSDN博客

二.定义节点

节点与二叉平衡树的节点差不多,多了一个平衡因子,一个父节点。

static class TreeNode {
    public int val;
    public int bf;//平衡因子
    public TreeNode left;
    public TreeNode right;
    public TreeNode parent;

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

三.插入操作

因为AVL树也是二叉平衡树,所以插入操作是一样的,只需在后面加一个调整平衡因子的操作。

//找到要插入的位置
TreeNode node = new TreeNode(val);
if(root == null) {
    root = node;
    return true;
}

TreeNode parent = null;
TreeNode cur = root;
while (cur != null) {
    if(cur.val < val) {
        parent = cur;
        cur = cur.right;
    }else if(cur.val == val) {
        return false;
    }else {
        parent = cur;
        cur = cur.left;
    }
}

//插入节点
node.parent = parent;
cur = node;
if(parent.val < val) {
    parent.right = node;
}else {
    parent.left = node;
}

上述代码就是插入节点的操作。插入完后我们要对平衡因子进行调整。

1.调整平衡因子

平衡因子可分为三种情况:\pm 2\pm 10

1.1 等于0,说明该节点的左右子树高度相同,高度相同也就意味着该节点平衡了,也就是说新插入的节点没有使树的高度发生变化,那么整个树都是平衡的。

1.2 等于\pm 1,说明该节点的左右子树高度相差1,如果左子树高那么就是-1,如果右子树高,那么就是1。如果是这种情况,还要继续往上找,因为这说明我们插入的节点影响了树的高度,这是要看一下是不是不平衡了。

1.3 等于\pm 2,说明该节点左右子树高度相差2,不平衡了,要进行调整。这里又要分情况讨论了。

当平衡因子等于 2 时,说明右子树高。这里又要分为两种情况:

为什么要分为这两种呢?这与加下来的旋转操作有关。

前面说了,AVL树就是靠旋转来进行调整以达到平衡。如左图右子树高,我们可以通过左旋来降低右子树的高度。这里大家可以去下面看一下左旋的具体操作。

但对于右图来说,左旋就不好用了,转了之后还是不平衡的。对于右图我们要用先右旋在左旋的操作。

为什么会这样?左旋转的本质就是将bf为\pm 1的左子树接到parent节点的右子树,如果说其这个左子树本身就是更深的子树,那么接上就和原来没有什么区别。

当平衡因子等于 -2 时,也是一样,都是一个原理这里不过多赘述,直接上代码。

public boolean insert(int val) {
    //找到要插入的位置
    TreeNode node = new TreeNode(val);
    if(root == null) {
        root = node;
        return true;
    }

    TreeNode parent = null;
    TreeNode cur = root;
    while (cur != null) {
        if(cur.val < val) {
            parent = cur;
            cur = cur.right;
        }else if(cur.val == val) {
            return false;
        }else {
            parent = cur;
            cur = cur.left;
        }
    }

    //插入节点
    node.parent = parent;
    cur = node;
    if(parent.val < val) {
        parent.right = node;
    }else {
        parent.left = node;
    }


    //调整平衡因子
    while (parent != null) {
        //更新平衡因子
        if(cur == parent.right) {
            parent.bf++;
        }else {
            parent.bf--;
        }

        if(parent.bf == 1 || parent.bf == -1){
            //继续循环
            cur = parent;
            parent = cur.parent;
        }else if(parent.bf == 2){
            if(cur.bf == 1) {
                rotateLeft(parent);
            }else {
                rotateRL(parent);
            }
            break;
        }else if(parent.bf == -2){
            if(cur.bf == -1) {
                rotateRight(parent);
            }else {
                rotateLR(parent);
            }
            break;
        }else{
            //已经平衡了
            break;
        }

    }
    return true;
}

2.左旋

将子树向左旋转:

上图左图是没有旋转时的状态,右图时左旋后的状态,我们可以通过节点变化来得到整个过程的变化:12的左子树连接到了10上,10变成了12的左子树。

可以拆成这么几步:

1.将bf=1的节点的左子树接到parent的右子树上;

2.将bf=1的节点连接到parent的parent;

3.将parent连接到bf=1的左子树上。

private void rotateLeft(TreeNode parent) {
        TreeNode subR = parent.right;
        TreeNode subRL = subR.left;

        //将bf=1的节点的左子树接到parent的右子树上
        parent.right = subRL;
        if(subRL != null) {
            subRL.parent = parent;
        }

        //将bf=1的节点连接到parent的parent
        TreeNode pParent = parent.parent;
        if(root == parent) {
            root = subR;
            root.parent = null;
        }else {
            if(pParent.left == parent) {
                pParent.left = subR;
            }else {
                pParent.right = subR;
            }
            subR.parent = pParent;
        }

        //将parent连接到bf=1的左子树上
        subR.left = parent;
        parent.parent = subR;

        //调整平衡因子
        subR.bf = parent.bf = 0;
    }

3.右旋

将子树向右旋:

思路跟向左旋一样,这里是将8的右子树连在10的左子树上,将10连在8的右子树上。

具体步骤:

1.将bf=-1的节点的右子树连在parent的左子树上;

2.将bf=-1的节点与parent的parent连接;

3.将parent连接到bf=-1节点的右子树上。

private void rotateRight(TreeNode parent) {
    TreeNode subL = parent.left;
    TreeNode subLR = subL.right;
    
    //将bf=-1的节点的右子树连在parent的左子树上
    parent.left = subLR;
    if(subLR != null) {
        subLR.parent = parent;
    }

    //将bf=-1的节点与parent的parent连接
    TreeNode pParent = parent.parent;
    if(parent == root) {
        root = subL;
        root.parent = null;
    }else {
        if(pParent.left == parent) {
            pParent.left = subL;
        }else {
            pParent.right = subL;
        }
        subL.parent = pParent;
    }

    //将parent连接到bf=-1的节点上
    subL.right = parent;
    parent.parent = subL;

    //调整平衡因子
    subL.bf = 0;
    parent.bf = 0;
}

4.先右旋后左旋

先旋转以bf=-1为父节点的树,再旋转parent的树:

表现在这张图里的是先旋转13节点的树,旋转完后再旋转10节点的树。

这里要特别说明以下平衡因子的调整:

上面两张图相当清晰表示出了平衡因子的变化。

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;
    }
    if(bf == -1){
        parent.bf = 0;
        subR.bf = 1;
        subRL.bf = 0;
    }
}

5.先左旋后右旋

这个跟先右旋再左旋相似,都很像。

代码:

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;
    }
    if(bf == 1){
        subL.bf = -1;
        subLR.bf = 0;
        parent.bf = 0;
    }
}

四.判断是不是AVL树

判断什么是不是什么这种问题一般是从性质出发。

判断是不是AVL树,首先这棵树是一颗二叉平衡树,其次这棵树的高度也要平衡。

public boolean isBalanced(TreeNode root) {
    if(root == null){
        return true;
    }
    int leftH = height(root.left);
    int rightH = height(root.right);

    if(rightH-leftH != root.bf) {
        return false;
    }

    return Math.abs(leftH-rightH) <= 1
            && isBalanced(root.left)
            && isBalanced(root.right);
}

五.总结

AVL树改善了原来二叉平衡树查找的问题,但也有新的问题。我们要在AVL树上插入或删除时,要不断的转转转,这个转转转也要时间的。所以说,如果我们要存储一个要频发插入删除的树,不适合用这个。

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

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

相关文章

基于 KubeSphere 的 Kubernetes 生产环境部署架构设计及成本分析

转载&#xff1a;基于 KubeSphere 的 Kubernetes 生产环境部署架构设计及成本分析 前言 导图 1. 简介 1.1 架构概要说明 今天分享一个实际小规模生产环境部署架构设计的案例&#xff0c;该架构设计概要说明如下&#xff1a; 本架构设计适用于中小规模(<50)的 Kubernetes …

本地生活服务商公司有哪些?一文教你搭建本地生活系统!

当前&#xff0c;本地生活领域群雄环伺&#xff0c;日益激烈的竞争推动各家互联网大厂调整布局模式的同时&#xff0c;也让本地生活市场持续迸发新的活力。在此背景下&#xff0c;想要通过本地生活服务商身份入局的创业者数量不断增多&#xff0c;以本地生活服务商公司有哪些等…

前端面试题整理-CSS

两栏布局 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>两栏布局</title><style>…

java计算机毕设课设—基于网络爬虫技术的网络新闻分析系统(附源码、文章、相关截图、部署视频)

这是什么系统&#xff1f; java计算机毕设课设—基于网络爬虫技术的网络新闻分析系统 基于网络爬虫技术的新闻分析系统&#xff0c;它能够实时抓取凤凰网、网易、新浪、搜狐等网站的新闻数据&#xff0c;提取正文和点击量&#xff0c;每日定时抓取。系统还能对抓取的新闻进行…

给echarts图表线条、数据点和区域设置颜色

let myChart echarts.init(document.getElementById("chartmainCop"));// 获取当前干部的各项评分const allIndicators Object.keys(this.dialogEacherTable[0]).filter(key > key ! "CadreID" && key ! "xm").map(key > ({name…

window电脑上使用python将pdf转换为word文档

1、电脑上安装Python运行环境 一、python官网下载链接 二、下载到电脑后&#xff0c;直接运行安装 三、安装完成后按&#xff1a;winR键进入window命令控制窗口&#xff0c;输入 python --version2、设置python依赖包国内镜像源 pip config set global.index-url https://mirr…

国家发改委区域司韩振海副司长一行莅临麒麟信安调研

7月31日&#xff0c;国家发改委区域司韩振海副司长一行莅临麒麟信安调研。湖南省发改委区域处处长孙健军&#xff0c;长沙市发改委党组成员、市长株潭一体化发展事务中心主任邹犇淼等相关领导陪同调研。麒麟信安总裁刘文清热情接待。 在麒麟信安展厅&#xff0c;韩振海副司长一…

在MANET中的TCP增强

本文内容节选自一篇系统性文献综述&#xff08;Systematic Literature Review, SLR&#xff09;&#xff0c;标题为“TCP Performance Enhancement in IoT and MANET”&#xff0c;由 Sultana Parween 和 Syed Zeeshan Hussain 撰写&#xff0c;发表在《International Journal …

Windows下Rust OpenCV环境配置

首发于Enaium的个人博客 安装Chocolatey 首先我们需要安装Chocolatey&#xff0c;Chocolatey是一个Windows的包管理器。 我们点击右上角的Install进入到Installing Chocolatey&#xff0c;选择Individual 复制命令 Set-ExecutionPolicy Bypass -Scope Process -Force; [Sys…

springboot餐饮管理系统-计算机毕业设计源码73168

摘要 随着科技的不断进步和互联网时代的深入发展&#xff0c;餐饮行业正面临着一场由传统向智能化、信息化转变的革命。传统的餐饮管理方式&#xff0c;如手工点餐、纸质菜单、人工结算等&#xff0c;已经无法满足现代餐饮企业对于效率、准确性和用户体验的高要求。因此&#x…

【Hot100】LeetCode—31. 下一个排列

目录 题目1- 思路2- 实现⭐31. 下一个排列——题解思路 3- ACM 实现 题目 原题连接&#xff1a;31. 下一个排列 1- 思路 技巧题&#xff0c;分为以下几个步骤 ① 寻找拐点&#xff1a; i 1 &#xff1a;出现 nums[i1] > nums[i] &#xff0c;则 i 1 就是拐点 从右向左遍…

技术守护尊严||Chat GPT在抵抗性骚扰的作用分析

就在本周&#xff0c;中国人民大学女博士实名举报导师性骚扰的事情&#xff0c;引发全网关注&#xff01; 性骚扰&#xff0c;无论在线上还是线下&#xff0c;无论在职场还是校园&#xff0c;都是对个人尊严与权益的严重侵犯。 幸运的是&#xff0c;随着人工智能技术的飞速发…

专题九_链表(1)

目录 题型总结 2. 两数相加 解析 题解 24. 两两交换链表中的节点 解析 题解 题型总结 2. 两数相加 2. 两数相加 解析 题解 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr)…

硬件开发笔记(二十九):TPS54331电源设计(二):12V转3.3V和12V转4V原理图设计

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140868749 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

一款功能强大且免费的跨平台图片批量处理工具

XnConvert是一款功能强大且免费的跨平台图片批量处理工具&#xff0c;广泛应用于个人用户、教育机构和非营利组织。它支持超过500种图片格式&#xff0c;包括常见的JPEG、PNG、TIFF、GIF、WebP、PSD、JPEG2000等&#xff0c;并能够导出为大约70种不同的文件格式。 该软件的主要…

【云原生】kubernetes弃用docker之后,containerd何以承载云原生?

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

Mac OS平台,利用 gifify 制作gif教程

一、前言 在很多时候都会用到视频的方式才能直观的表达想表达的东西&#xff0c; 但是视频的文件太大了&#xff0c;所以gif是一个很不错的选择&#xff0c;在网上找了很多免费的都不好用&#xff0c; 最理想的还是直接快捷键唤起&#xff0c;然后选择录制区域&#xff0c;保存…

[CR]厚云填补_GridDehazeNet

GridDehazeNet: Attention-Based Multi-Scale Network for Image Dehazing Abstract 我们提出了一个端到端的可训练卷积神经网络(CNN)&#xff0c;命名为GridDehazeNet&#xff0c;用于单幅图像去雾。GridDehazeNet由预处理、主干网络和后处理三个模块组成。与手工选择的预处理…

Go语言---linux下安装golang protoc详细教程以及完整安装protoc-gen-go工具

在[分布式网络通讯框架]----Protobuf安装配置–附带每一步截图中&#xff0c;我们详细介绍了Protobuf是什么&#xff0c;为什么要使用Protobuf&#xff0c;以及在linux环境中&#xff0c;如何安装Protobuf。Protobuf 在 .proto 定义需要处理的结构化数据&#xff0c;可以通过 p…

Surya - OCR、布局分析、阅读顺序、语言检测

文章目录 一、关于 Surya功能特性例子训练托管API商业用途 二、安装手动安装 三、用法1、交互应用2、OCR&#xff08;文本识别&#xff09;来自 python编译 3、文本行检测From python 4、布局分析From python 5、阅读顺序From python 四、限制五、故障排除六、基准测试OCRGoogl…