数据结构 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));}
}

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为组播配置案例

igmp-snooping主要用于生成二层组播表项&#xff0c;防止交换机全部接口都发组播报文 PC端配置&#xff1a; 组播源配置&#xff1a; R1 interface GigabitEthernet0/0/0 ip address 10.0.0.1 255.255.255.0 pim dm interface GigabitEthernet0/0/1 ip address 192.168.0…

【深度学习目标检测】八、基于yolov5的抽烟识别(python,深度学习)

YOLOv5是目标检测领域一种非常优秀的模型&#xff0c;其具有以下几个优势&#xff1a; 1. 高精度&#xff1a;YOLOv5相比于其前身YOLOv4&#xff0c;在目标检测精度上有了显著的提升。YOLOv5使用了一系列的改进&#xff0c;如更深的网络结构、更多的特征层和更高分辨率的输入图…

GD32烧录第一盏灯

学习目标 掌握基本开发流程掌握程序编译掌握程序烧录掌握GPIO初始化流程学习内容 开发流程 项目新建代码编写程序烧录验证结果需求分析 点亮LED1灯,并且闪烁。 项目新建 📎GD32Template.zip 附件为模板代码,解压后修改项目名称。 进入Project

【从零开始学习--设计模式--策略模式】

返回首页 前言 感谢各位同学的关注与支持&#xff0c;我会一直更新此专题&#xff0c;竭尽所能整理出更为详细的内容分享给大家&#xff0c;但碍于时间及精力有限&#xff0c;代码分享较少&#xff0c;后续会把所有代码示例整理到github&#xff0c;敬请期待。 此章节介绍策…

数据仓库与数据挖掘小结

更加详细的只找得到pdf版本 填空10分 判断并改错10分 计算8分 综合20分 客观题 填空10分 判断并改错10分--错的要改 mooc中的--尤其考试题 名词解释12分 4个&#xff0c;每个3分 经常碰到的专业术语 简答题40分 5个&#xff0c;每道8分 综合 画roc曲线 …

怎么将文件变为可执行文件

怎么将文件变为可执行文件 在Unix/Linux系统中&#xff0c;要将一个文件变为可执行文件&#xff0c;你需要使用chmod命令。以下是基本的步骤&#xff1a; 打开终端&#xff1a;使用你系统中的终端或命令行界面。 使用 cd 命令切换到包含你的文件的目录。例如&#xff1a; bash …

106基于matlab的粒子群算法与 Simulink 模型之间连接的桥梁是粒子(即 PID 控制器参数)和该粒子对应的适应值(即控制系统的性能指标)

基于matlab的粒子群算法与 Simulink 模型之间连接的桥梁是粒子&#xff08;即 PID 控制器参数&#xff09;和该粒子对应的适应值&#xff08;即控制系统的性能指标&#xff09;。优化过程如下&#xff1a;PSO 产生粒子群&#xff08;可以是初始化粒子群&#xff0c;也可以是更新…

阿里云部署k8s with kubesphere

阿里云ESC 创建实例 填入密码即可 云上的防火墙相关设置就是安全组 vpc 专有网络 划分私有ip 子网 vpc 隔离环境域 不同的vpc下 即使相同的子网也不互通 使用交换机继续划分子网 停止 释放 不收钱 k8s 服务器 4核8G*1 8核16G *2 git 创建凭证 pipeline 发邮箱 (p124)…

数据结构:直接选择排序和堆排序

直接选择排序&#xff1a; 这里我用两个变量同时找出最小值和最大值。 注意&#xff1a;若begin为最大值&#xff0c;maxi即为最大值的下标&#xff0c;若将最小值与其交换&#xff0c;最大值的下标此时就不再是maxi&#xff0c;而变为mini了&#xff0c;故此时要调整maxi的位…

关于pycharm无法进入base界面的问题

问题&#xff1a;terminal输入activate无法进入base 解决方案 1.Cortana这边找到Anaconda Prompt右击进入文件所在位置 2. 右击进入属性 3. 复制cmd.exe开始到最后的路径 cmd.exe "/K" C:\ProgramData\anaconda3\Scripts\activate.bat C:\ProgramData\anaconda3 …

系统架构设计师教程(七)系统架构设计基础知识

系统架构设计基础知识 7.1 软件架构概念7.1.1 软件架构的定义7.1.2 软件架构设计与生命周期需求分析阶段设计阶段实现阶段构件组装阶段部署阶段后开发阶段 7.1.3 软件架构的重要性 7.2 基于架构的软件开发方法7.2.1 体系结构的设计方法概述7.2.2 概念与术语7.2.3 基于体系结构的…

【Spark精讲】Spark Shuffle详解

目录 Shuffle概述 Shuffle执行流程 总体流程 中间文件 ShuffledRDD生成 Stage划分 Task划分 Map端写入(Shuffle Write) Reduce端读取(Shuffle Read) Spark Shuffle演变 SortShuffleManager运行机制 普通运行机制 bypass 运行机制 Tungsten Sort Shuffle 运行机制…