数据结构进阶——AVL树

一、AVL树基本概念

1.1定义

如果一颗二叉搜索树的左右子树的高度差的绝对值不超过1(1,0,-1),那么这颗二叉搜索树就叫AVL树。


 

1.2AVL树的性质

 AVL树的左右子树也是一颗AVL树,二叉搜索树是一颗高度平衡的二叉树,它查找值的时间复杂度可以保持在树的高度

 


二、AVL树的实现

2.1AVL树节点的定义

static class TreeNode {public int val;public TreeNode left;public TreeNode right;public TreeNode parent;//记录当前节点的父亲节点public int bf;//平衡因子public TreeNode(int val) {this.val = val;}}

注意:平衡因子的值为右子树高度减去左子树的高度。


 2.2AVL树的插入

AVL树的插入逻辑与二叉搜索树的插入逻辑相同,都是通过与节点比较大小来确定新节点的插入位置,但是对于AVL树,每插入一个节点都需要更新这个节点的父亲节点和这个节点的祖先节点的平衡因子

如果平衡因子大于1,就需要根据平衡因子的值来确定具体的旋转方法:

<1>左单旋:新节点插入到较高右子树的右边。

<2>右单旋:新节点插入到较高左子树的左边。

<3>左右双旋:新节点插入到较高左子树的右边。

<4>右左双旋:新节点插入到较高右子树的左边。 

!!! 可以看到,AVL树的插入会涉及到树的旋转操作,也就是说,如果大量插入节点或者大量删除节点都会涉及到树的多次旋转,导致时间复杂度过高,因此AVL树只适合用来存储一些不会频繁更新的元素,对于AVL树,主要利用的是它的查找效率


2.3AVL树插入代码实现

 下面是四种旋转方法示意图:

 

 

 

代码实现:

 

class MyAvlTree {static class TreeNode {public int val;public TreeNode left;public TreeNode right;public TreeNode parent;//记录当前节点的父亲节点public int bf;//平衡因子public TreeNode(int val) {this.val = val;}}public TreeNode root;//根结点//向avl树插入值public boolean insert(int val) {if (root == null) {root = new TreeNode(val);return true;}TreeNode node = new TreeNode(val);TreeNode cur = root;TreeNode prev = null;//记录cur的父亲节点while (cur != null) {if (cur.val < val) {prev = cur;cur = cur.right;} else if (cur.val > val) {prev = cur;cur = cur.left;} else {return false;}}//判断最后一个节点的值的大小if (prev.val < val) {prev.right = node;} else {prev.left = node;}//插入一个节点后从新插入节点的父亲节点开始调节平衡因子node.parent = prev;cur = node;//调节平衡因子while (prev !=null) {//调节到根节点结束//通过判断cur是prev的左树还是右树,决定平衡因子++还是--if (cur == prev.right) {prev.bf++;} else {prev.bf--;}if (prev.bf == 0) {//说明已经平衡了,不需要再向上调整平衡因子break;} else if (prev.bf == 1 || prev.bf == -1) {//继续向上调整,修改平衡因子cur = prev;prev = prev.parent;} else {if (prev.bf == 2) {//说明右树高,需要将右树的节点给一部分给左树if (cur.bf == 1) {//左旋rotateRight(prev);} else {//右左双旋rotateRL(prev);}} else {//平衡因子为-2,说明左树高,将左树的节点给一部分给右树if (cur.bf == -1) {//右旋rotateRight(prev);} else {//左右双旋rotateLR(prev);}}//到这里说明平衡了break ;}}return true;}private void rotateRL(TreeNode prev) {TreeNode subL = prev.left;TreeNode subLR = subL.right;int bf = subLR.bf;rotateRL(prev.left);rotateRL(prev);if (bf == -1) {subLR.bf = 0;prev.bf = 1;subL.bf = 0;} else if(bf == 1){subLR.bf = 0;prev.bf = 0;subL.bf = -1;}}private void rotateLR(TreeNode prev) {TreeNode subL = prev.left;TreeNode subLR = subL.right;int bf = subLR.bf;rotateLeft(prev.left);rotateRight(prev);//调整平衡因子if (bf == -1) {subL.bf = 0;prev.bf = 1;subLR.bf = 0;} else if(bf == 1){subLR.bf = 0;subL.bf = -1;prev.bf = 0;}}//右单旋private void rotateRight(TreeNode prev) {TreeNode subL = prev.left;TreeNode subLR = subL.right;prev.left = subLR;subL.right = prev;prev.parent = subL;//没有subLR节点if (subLR != null) {subLR.parent = prev;}if (prev == root) {//检查prev是否为根结点subL.parent = null;root = subL;} else {TreeNode Pparent = prev.parent;//记录prev的父亲节点if (Pparent.left == prev) {//如果prev是它父亲节点的左节点Pparent.left = subL;subL.parent = Pparent;} else {//如果prev是它父亲节点的右节点Pparent.right = subL;subL.parent = Pparent;}}//修改平衡因子prev.bf = 0;subL.bf = 0;}//左单旋private void rotateLeft(TreeNode prev) {TreeNode subR = prev.right;TreeNode subRL = subR.left;prev.right = subRL;subR.left = prev;prev.parent = subR;if (subRL != null) {subRL.parent = prev;}if (prev == root) {subR.parent = null;root = subR;} else {TreeNode Pparent = prev.parent;if (Pparent.left == prev) {subR.parent = Pparent;Pparent.left = subR;} else {subR.parent = Pparent;Pparent.right = subRL;}}//修改平衡因子prev.bf = 0;subR.bf = 0;}
}

2.4AVL树的验证 

如果一个二叉树的节点中没有记录平衡因子,就需要通过递归来计算左右子树高度差,如果不大于1,说明是平衡二叉树,否则不是平衡二叉树。

具体代码如下: 

 private boolean isBalance(TreeNode root) {if (root == null) return true;int leftH = height(root.left);int rightH = height(root.right);//检查平衡因子if (rightH - leftH != root.bf) {System.out.println(root.val + " 平衡因子异常!");return false;}return Math.abs(leftH - rightH) <= 1 &&isBalance(root.left) && isBalance(root.right);}

2.5AVL树性能分析

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要 维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合

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

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

相关文章

Easyconnect官网下载安装使用教程

软件下载 打开官网https://www.sangfor.com/downloads&#xff1b; 选择自己熟悉的语言 windows选择支持与服务—软件下载 mac 找售后智能在线&#xff08;见下面MAC&#xff09; 在所有产品与服务中选择&#xff1a;SSL VPN 左侧选择SSL VPN工具&#xff0c;然后根据自…

从神经元到神经网络:深度学习的进化之旅

神经元、神经网络 神经元 Neuron )&#xff0c;又名感知机( Perceptron )&#xff0c;在模型结构上与 逻辑回归 一致&#xff0c;这里以一个二维输入量的例子对其进行进一步 的解释&#xff1a; 假设模型的输 入向 量是一 维特征向 (x1,x2). 则单神 经元的模型结构 如下…

人工智能(AI)和机器学习(ML)技术学习流程

目录 人工智能(AI)和机器学习(ML)技术 自然语言处理(NLP): Word2Vec: Seq2Seq(Sequence-to-Sequence): Transformer: 范式、架构和自注意力: 多头注意力: 预训练、微调、提示工程和模型压缩: 上下文学习、思维链、全量微调、量化、剪枝: 思维树、思维…

Odoo:免费开源的医药流通行业信息化解决方案

文 / 开源智造Odoo亚太金牌服务 方案概述 开源智造Odoo免费开源ERP提供面向医药批发采、供、销业财一体化&#xff0c;及直接面向消费者的门店终端、全渠道管理、营销管理以及GSP合规管理解决方案&#xff0c;提升企业运营效率和全业务链条的数字化管控、追溯能力。 行业的最新…

牛客sql题目总结(1)

1.第N高的薪水 AC: create function getnthhighestsalary(n int) returns int begindeclare m int; set m n - 1; return (select distinct salaryfrom employeeorder by salary desclimit m, 1); end 2.平均播放进度大于60%的视频类别 AC&#xff1a; select tb_video_info…

数量少的连锁店要不要用智能巡检?

无论是在新闻报道中&#xff0c;还是企业定制目标客户时&#xff0c;人们都更喜欢聚焦原本就已经站在各行业金字塔尖的那 1%&#xff0c;剩下的 99% 却常常被忽略。 比如此刻我正在搜索中小型连锁企业智能巡检相关的资讯&#xff0c;但网页展示的结果基本围绕着「中大型、1000门…

windows 进程降权和提权代码示例(2)

强制完整性控制 - Win32 应用程序 |Microsoft 学习 一、强制完整性控制 品03/26/20217 个参与者 反馈 本文内容 诚信标签进程创建强制性政策 强制完整性控制 &#xff08;MIC&#xff09; 提供了一种用于控制对安全对象的访问的机制。此机制是对自主访问控制的补充&#xff…

Redis - Set 集合

一、基本了解 集合类型也是保存多个字符串类型的元素的&#xff0c;但和列表类型不同的是&#xff0c;集合中1&#xff09;元素之间是⽆序 的2&#xff09;元素不允许重复&#xff0c;如图2-24所⽰。⼀个集合中最多可以存储 32 2 − 1 个元素。Redis除了⽀持 集合内的增删查改…

Java教学辅助:SpringBoot平台实战技巧

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统&#xff0c;它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等&#xff0c;非常…

Codeforces Round 970(Div. 3) (预处理后缀, 一道适合py的题)

F. Sakurakos Box 传送门&#xff1a;Problem - 2008F - Codeforces Sakurako has a box with nn balls. Each ball has its value. She wants to bet with her friend that if the friend randomly picks two balls from the box (it could be two distinct balls, but they…

OpenDroneMap Webodm

OpenDroneMap & Webodm OpenDroneMap Webodm 开源无人机航拍系列图像及其它系列图像三维重建软件。很棒的开源无人机测绘软件OpenDroneMap,从航拍图像生成精确的地图、高程模型、3D 模型和点云。 应用领域 Mapping & Surveying 测绘和测量 从图像测量获得高精度的可…

Github 2024-11-02 Rust开源项目日报 Top10

根据Github Trendings的统计,今日(2024-11-02统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Python项目2Dart项目1RustDesk: 用Rust编写的开源远程桌面软件 创建周期:1218 天开发语言:Rust, Dart协议类型:GNU Affero Genera…

前端八股文(四)计网 持续更新中。。。

计网相关面试题 1.http缓存的方式 缓存是为了重复使用而被存储的&#xff0c;可以减少浏览器和服务器之间通信的次数、降低网络延迟、加速页面加载、提高用户体验性等。不但能使网页打开速度更快&#xff0c;还能减少服务器的压力。 浏览器缓存策略&#xff1a; 强缓存&…

项目一:使用 Spring + SpringMVC + Mybatis + lombok 实现网络五子棋

一&#xff1a;系统展示: 二&#xff1a;约定前后端接口 2.1 登陆 登陆请求&#xff1a; GET /login HTTP/1.1 Content-Type: application/x-www-form-urlencodedusernamezhangsan&password123登陆响应&#xff1a; 正常对象&#xff1a;正常对象会在数据库中存储&…

vue 插槽

参考文档 插槽 Slots | Vue.js 1. 基本概念 Vue的插槽&#xff08;slot&#xff09;&#xff0c;简单来说&#xff0c;就是一种 定义在组件中的 “占位符”。用于实现现组件的内容分发和复用。如下&#xff0c;是一个简单的默认插槽&#xff1a; <!-- Parent.vue --> &…

信息流不同行业账户流量池有区别吗?

在投放过程中&#xff0c;我们经常遇到这么一个问题&#xff0c;不同行业账户投放&#xff0c;流量池会有区别嘛&#xff1f;我认为是有的&#xff0c;那么对于我们而言&#xff0c;怎么样才能利用好媒体对于流量池的划分效果&#xff0c;可以从以下几个方面来进行考虑&#xf…

[Tex] Ubuntu 搭建 TexWork

更新软件库 打开终端&#xff1a; sudo apt --update sudo apt --upgrade 安装 texlive 完整版与 TexWorks 界面 sudo apt install texlive-full sudo apt install texworks

从0开始深度学习(26)——汇聚层/池化层

池化层通过减少特征图的尺寸来降低计算量和参数数量&#xff0c;同时增加模型的平移不变性和鲁棒性。汇聚层的主要优点之一是减轻卷积层对位置的过度敏感。 1 最大汇聚层、平均汇聚层 汇聚层和卷积核一样&#xff0c;是在输入图片上进行滑动计算&#xff0c;但是不同于卷积层的…

地图带你看三山五岳-基于Leaflet的重点旅游专题实现

目录 前言 一、关于三山五岳 1、三山五岳简介 2、位置信息检索 二、使用Leaflet进行WebGIS标注 1、基础数据准备 2、点位标绘 三、实际效果 1、整体效果 2、东岳泰山 3、西岳华山 4、南岳衡山 5、北岳恒山 6、 中岳嵩山 四、总结 前言 在信息技术飞速发展的今…

营销邮件策略:提升打开率和转化率的技巧!

营销邮件的发送技巧有哪些&#xff1f;如何提高营销邮件召唤力&#xff1f; 随着邮件数量的激增&#xff0c;如何确保您的营销邮件能够脱颖而出&#xff0c;提升打开率和转化率&#xff0c;成为了每个营销人员必须面对的挑战。MailBing将深入探讨一系列有效的营销邮件策略&…