当前位置: 首页 > news >正文

【计算机基础——数据结构——AVL平衡二叉树】

1. BST二叉查找树

1.1 BST二叉查找树的特性

  • 左子树上所有结点的值均小于或等于它的根结点的值。
  • 右子树上所有结点的值均大于或等于它的根结点的值。
  • 左、右子树也分别为二叉排序树。

1.2 BST二叉查找树的缺点

  • 二叉查找树是有缺点的,在不断插入的时候,有可能出现这样一种情况:很容易“退化”成链表,
  • 如果bst 树的节点正好从大到小的插入,此时树的结构也类似于链表结构,这时候的查询或写入耗时与链表相同。

在这里插入图片描述
为了避免这种特殊的情况发生,引入了平衡二叉树(AVL)和红黑树(red-black tree)

2. AVL平衡二叉树

平衡二叉树也叫AVL(发明者名字简写),也属于二叉搜索树的一种,与其不同的是AVL通过机制保证其自身的平衡。

AVL树是最先发明的自平衡二叉查找树。
在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。
增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。

2.1 AVL树的特性

AVL树本质上还是一棵二叉搜索树,它有以下特性:

  • 特性1: 对于任何一颗子树的root根结点而言,它的左子树任何节点的key一定比root小,而右子树任何节点的key 一定比root大;
  • 特性2:对于AVL树而言,其中任何子树仍然是AVL树;
  • 特性3:每个节点的左右子节点的高度之差的绝对值最多为1;

在插入、删除树节点的时候,如果破坏了以上的原则,AVL树会自动进行调整使得以上三条原则仍然成立。

2.2 AVL树的平衡功能

举个例子,下左图为AVL树最长的2节点与最短的8节点高度差为1;

当插入一个新的节点后,根据上面第一条原则,它会出现在2节点的左子树,但这样一来就违反了原则3。

在这里插入图片描述
此时AVL树会通过节点的旋转进行进行平衡,

AVL调整的过程称之为左旋和右旋,左旋就是逆时针转,右旋是顺时针转

2.3 AVL平衡的调整过程

调整的过程:

  1. 确定旋转支点(pivot):这个旋转支点就是失去平衡这部分树,在自平衡之后的根节点,平衡的调整过程,需要根据pivot它来进行旋转。我们只关注失衡子树的根结点 及它的子节点和孙子节点即可。
  2. 判断AVL失衡的类型:包含左左结构失衡(LL型失衡)、右右结构失衡(RR型失衡)、左右结构失衡(LR型失衡)、右左结构失衡(RL型失衡)
  3. 根据失衡的类型进行相应的旋转

2.3.1 场景1:LL失衡-左左结构失衡(右旋)

在结点Root的 左结点(L) 的 左子树(L) 上做了插入元素的操作,我们称这种情况为 左左型 ,我们应该进行右旋转。
在这里插入图片描述

2.3.2 场景2:RR型失衡:右右结构失衡(左旋)

在结点Root的 右结点(R) 的 右子树(R) 上做了插入元素的操作,我们称这种情况为右右型 ,我们应该进行左旋转。
在这里插入图片描述

2.3.3 场景3: LR型失衡:左右失衡(左旋+右旋):

在结点Root的 左结点(L) 的 右子树(R) 上做了插入元素的操作,我们称这种情况为左右型 ,我们应该进行左旋转+右旋转。
在这里插入图片描述

2.3.4 场景4:RL失衡: 右左结构 (右旋+左旋):

在结点Root的 右结点(R) 的 左子树(L) 上做了插入元素的操作,我们称这种情况为右左型 ,我们应该进行右旋转+左旋转。
在这里插入图片描述

3. 代码实现+详细注解

3.1 基本结构+操作

package mainimport "fmt"// AVL树的节点
type Node struct {Key    intHeight intLeft   *NodeRight  *Node
}type AVLTree struct {Root *Node
}// 返回节点的高度
func height(n *Node) int {if n == nil {return 0}return n.Height
}func max(a, b int) int {if a > b {return a}return b
}// 返回当前节点的平衡因子
func getBalance(n *Node) int {if n == nil {return 0}return height(n.Left) - height(n.Right)
}// 右旋
func rightRotate(root *Node) *Node {//pivot是新的根节点,T2是要转移的子树的根pivot := root.LeftT2 := pivot.Rightpivot.Right = rootroot.Left = T2// 重新计算两个调整后的节点高度root.Height = max(height(root.Left), height(root.Right)) + 1pivot.Height = max(height(pivot.Left), height(pivot.Right)) + 1return pivot
}// 右旋
func leftRotate(root *Node) *Node {//pivot是新的根节点,T2是要转移的子树的根pivot := root.RightT2 := pivot.Leftpivot.Left = rootroot.Right = T2// 重新计算两个调整后的节点高度root.Height = max(height(root.Left), height(root.Right)) + 1pivot.Height = max(height(pivot.Left), height(pivot.Right)) + 1return pivot
}// 查找
func (t *AVLTree) Search(key int) *Node {return search(t.Root, key)
}func search(node *Node, key int) *Node {if node == nil {return nil}if key == node.Key {return node}if key < node.Key {return search(node.Left, key)}return search(node.Right, key)
}

3.2 插入操作

func (t *AVLTree) Insert(key int) {t.Root = insert(t.Root, key)
}func insert(node *Node, key int) *Node {// 1. 找到插入节点的位置if node == nil {return &Node{Key: key, Height: 1}}if key < node.Key {node.Left = insert(node.Left, key)} else if key > node.Key {node.Right = insert(node.Right, key)} else {//重复的key是不被允许的return node}// 2. 更新新插入节点的祖先节点的高度node.Height = max(height(node.Left), height(node.Right)) + 1// 3. 获得当前节点的平衡因子balance := getBalance(node)// 4. 平衡调整// 4.1说明新插入的节点插入到了当前节点的左节点的左孩子,需要进行右旋if balance > 1 && key < node.Left.Key {return rightRotate(node)}// 4.2说明新插入的节点插入到了当前节点的右节点的右孩子,需要进行左旋if balance < -1 && key > node.Right.Key {return leftRotate(node)}// 4.3说明新插入的节点插入到了当前节点的左节点的右孩子,需要进行左旋+右旋if balance > 1 && key > node.Right.Key {node.Left = leftRotate(node.Left)return rightRotate(node)}// 4.4说明新插入的节点插入到了当前节点的右节点的左孩子,需要进行右旋+左旋if balance < -1 && key < node.Left.Key {node.Right = rightRotate(node.Right)return leftRotate(node)}// 4.5 不要需要进行平衡调整return node
}

3.3 删除操作

func (t *AVLTree) Delete(key int) {t.Root = delete(t.Root, key)
}func delete(node *Node, key int) *Node {if node == nil {return nil}// 1. 找到要删除的节点if key < node.Key {node.Left = delete(node.Left, key)} else if key > node.Key {node.Right = delete(node.Right, key)} else {// 被删除的节点是叶子节点或者只有一个孩子节点if node.Left == nil {return node.Right} else if node.Right == nil {return node.Left}// 被删除的节点下面还有两个孩子节点// 先找到被删除节点下面最小的值temp := minValueNode(node.Right)// 将最小的值放到当前节点node.Key = temp.Key// 递归删除最小值node.Right = delete(node.Right, temp.Key)}if node == nil {return node}// 2. 更新新插入节点的祖先节点的高度node.Height = max(height(node.Left), height(node.Right)) + 1// 3. 获得当前节点的平衡因子balance := getBalance(node)// 4. 平衡调整// 4.1说明新删除的节点导致当前节点的平衡因子出了问题,//	  判断是当前节点左节点的左孩子造成的,需要进行右旋if balance > 1 && getBalance(node.Left) >= 0 {return rightRotate(node)}// 4.2说明新删除的节点导致当前节点的平衡因子出了问题,//	  判断是当前节点右节点的右孩子造成的,需要进行左旋if balance < -1 && getBalance(node.Right) <= 0 {return leftRotate(node)}// 4.3说明新删除的节点导致当前节点的平衡因子出了问题,//  判断是当前节点左节点的右孩子造成的,需要进行左旋+右旋if balance > 1 && getBalance(node.Left) < 0 {node.Left = leftRotate(node.Left)return rightRotate(node)}// 4.4说明新删除的节点导致当前节点的平衡因子出了问题,//    判断是当前节点右节点的左孩子造成的,需要进行右旋+左旋if balance < -1 && getBalance(node.Right) > 0 {node.Right = rightRotate(node.Right)return leftRotate(node)}// 4.5 不要需要进行平衡调整return node
}func minValueNode(node *Node) *Node {current := nodefor current.Left != nil {current = current.Left}return current
}

3.4 测试

func main() {tree := &AVLTree{}// 插入节点tree.Insert(10)tree.Insert(20)tree.Insert(30)tree.Insert(40)tree.Insert(50)tree.Insert(60)tree.Insert(70)// 层次遍历fmt.Println(LevelOrder(tree.Root))tree.Delete(10)tree.Delete(20)tree.Delete(30)fmt.Println(LevelOrder(tree.Root))
}// LevelOrder 返回层次遍历的结果(按层分组)
func LevelOrder(root *Node) [][]int {result := make([][]int, 0)if root == nil {return result}// 使用队列来存储节点queue := []*Node{root}for len(queue) > 0 {// 当前层的节点数levelSize := len(queue)// 存储当前层的值currentLevel := make([]int, 0, levelSize)// 遍历当前层的所有节点for i := 0; i < levelSize; i++ {// 获取队首节点node := queue[0]queue = queue[1:] // 出队// 将当前节点的值加入当前层的结果中currentLevel = append(currentLevel, node.Key)// 将子节点加入队列if node.Left != nil {queue = append(queue, node.Left)}if node.Right != nil {queue = append(queue, node.Right)}}// 将当前层的结果加入最终结果result = append(result, currentLevel)}return result
}

层次遍历的结果,符合预期
在这里插入图片描述

相关文章:

【计算机基础——数据结构——AVL平衡二叉树】

1. BST二叉查找树 1.1 BST二叉查找树的特性 左子树上所有结点的值均小于或等于它的根结点的值。右子树上所有结点的值均大于或等于它的根结点的值。左、右子树也分别为二叉排序树。 1.2 BST二叉查找树的缺点 二叉查找树是有缺点的&#xff0c;在不断插入的时候&#xff0c;…...

体育活动赛事报名马拉松微信小程序开发

功能描述 体育活动赛事报名马拉松微信小程序&#xff0c;该项目是一个体育活动报名小程序&#xff0c;主要功能有活动报名、扫码签到、签到积分、排行奖励、积分兑换等功能。 用户端&#x1f536;登录&#xff1a;◻️1.微信授权登录 ◻️2.手机号码授权 &#x1f536;首页&am…...

【C++】C++基础知识

一.函数重载 1.函数重载的概念 函数重载是函数的一种特殊情况&#xff0c;C允许在同一作用域中声明几个功能类似的同名函数&#xff0c;这些同名函数的形参列表必须不同。函数重载常用来处理实现功能类似&#xff0c;而数据类型不同的问题。 #include <iostream> using…...

中间件安全

IIS IIS短文件漏洞 此漏洞实际是由HTTP请求中旧DOS 8.3名称约定(SFN)的代字符(~)波浪号引起的。它允许远程攻击者在Web根目录下公开文件和文件夹名称(不应该可被访问)。攻击者可以找到通常无法从外部直接访问的重要文件,并获取有关应用程序基础结构的信息。 利用工具 https…...

Zabbix中文监控指标数据乱码

1&#xff09;点击主机&#xff0c;选择Zabbix server 中的 图形 一项&#xff0c;可以看到当前显示的为乱码 2&#xff09; 下载字体文件&#xff1a; https://gitcode.com/open-source-toolkit/4a3db/blob/main/SimHei.zip 解压unzip -x SimHei.zip 3&#xff09; 替换字体文…...

【AI】AI如何赋能软件开发流程

方向一&#xff1a;流程与模式介绍【传统软件开发 VS AI参与的软件开发】 传统软件开发流程 传统软件开发流程一般可以分为以下几个阶段&#xff1a; 1. 需求分析&#xff1a;在这个阶段&#xff0c;开发团队与客户沟通&#xff0c;明确软件的需求和目标。团队会收集、整理和分…...

恒创科技:什么是 RAID 3 ? RAID 3、4 和5之间有什么区别?

RAID 是一种存储数据以提高性能并减少数据丢失的特定技术。您可以根据自己的需求选择多种 RAID 类型。RAID 3 是列表中比较有效的类型之一。本文将重点介绍这种特定的 RAID 技术&#xff0c;并比较 RAID 3、4 和 5。 RAID 3 的定义 RAID 3 是一种特定的磁盘配置&#xff0c;用于…...

python获取iOS最近业务日志的两种方法

当iOS UI自动化用例执行失败的时候&#xff0c;需要获取当时的业务日志&#xff0c;供后续分析使用。 现在已经把iOS沙盒目录挂载到本地&#xff0c;剩下的事情就是从沙盒目录中捞取当前的日志&#xff0c;沙盒中的日志文件较大&#xff0c;整体导出来也可以&#xff0c;但是会…...

【如何获取股票数据43】Python、Java等多种主流语言实例演示获取股票行情api接口之沪深指数历史交易数据获取实例演示及接口API说明文档

最近一两年内&#xff0c;股票量化分析逐渐成为热门话题。而从事这一领域工作的第一步&#xff0c;就是获取全面且准确的股票数据。因为无论是实时交易数据、历史交易记录、财务数据还是基本面信息&#xff0c;这些数据都是我们进行量化分析时不可或缺的宝贵资源。我们的主要任…...

ESLint 使用教程(一):从零配置 ESLint

前言 在现代前端开发中&#xff0c;代码质量和风格一致性是团队合作和项目维护的重要因素。而 ESLint 作为一款强大的 JavaScript 静态代码分析工具&#xff0c;能够帮助开发者发现和修复代码中的潜在问题。本文将详细介绍 ESLint 的常用规则配置&#xff0c;并结合实际应用场…...

openssl对称加密代码讲解实战

文章目录 一、openssl对称加密和非对称加密算法对比1. 加密原理2. 常用算法3. 加密速度4. 安全性5. 应用场景6. 优缺点对比综合分析 二、代码实战代码说明&#xff1a;运行输出示例代码说明&#xff1a;注意事项 一、openssl对称加密和非对称加密算法对比 OpenSSL 是一个广泛使…...

web前端动画按钮(附源代码)

效果图 源代码 HTML部分 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> …...

go函数传值是值传递?还是引用传递?slice案例加图解

先说下结论 Go语言中所有的传参都是值传递&#xff08;传值&#xff09;&#xff0c;都是一个副本&#xff0c;一个拷贝。 值语义类型&#xff1a;参数传递的时候&#xff0c;就是值拷贝&#xff0c;这样就在函数中就无法修改原内容数据。 基本类型&#xff1a;byte、int、bool…...

PostgreSQL数据库笔记

PostgreSQL 是什么 PostgreSQL&#xff08;简称Postgres或PG&#xff09;是一个功能强大、可靠性高、可扩展性好的开源对象-关系数据库服务器&#xff08;ORDBMS&#xff09;&#xff0c;它以加州大学伯克利分校计算机系开发的POSTGRES版本4.2为基础。 发展历程 起源与发展&a…...

财务软件源码SaaS云财务

在如今的商业环境中&#xff0c;准确的财务管理是一家企业取得成功的关键。然而&#xff0c;传统的财务管理方法已经无法满足现代企业的需求&#xff0c;需要一个全新的解决方案。推出了全新的财务软件为您提供完美的解决方案。 选择财务软件源码&#xff0c;您将享受到以下优…...

Elasticsearch集群和Kibana部署流程

搭建Elasticsearch集群 1. 进入Elasticsearch官网下载页面&#xff0c;下载Elasticsearch 在如下页面选择Elasticsearch版本&#xff0c;点击download按钮&#xff0c;进入下载页面 右键选择自己操作系统对应的版本&#xff0c;复制下载链接 然后通过wget命令下载Elastics…...

丹摩征文活动 | 丹摩智算:大数据治理的智慧引擎与实践探索

丹摩DAMODEL&#xff5c;让AI开发更简单&#xff01;算力租赁上丹摩&#xff01; 目录 一、引言 二、大数据治理的挑战与重要性 &#xff08;一&#xff09;数据质量问题 &#xff08;二&#xff09;数据安全威胁 &#xff08;三&#xff09;数据管理复杂性 三、丹摩智算…...

【Django】Clickjacking点击劫持攻击实现和防御措施

Clickjacking点击劫持 1、clickjacking攻击2、clickjacking攻击场景 1、clickjacking攻击 clickjacking攻击又称为点击劫持攻击&#xff0c;是一种在网页中将恶意代码等隐藏在看似无害的内容&#xff08;如按钮&#xff09;之下&#xff0c;并诱使用户点击的手段。 2、clickj…...

Ansys Zemax | 手机镜头设计 - 第 4 部分:用LS-DYNA进行冲击性能分析

该系列文章将讨论智能手机镜头模组设计的挑战&#xff0c;从概念和设计到制造和结构变形分析。本文是四部分系列中的第四部分&#xff0c;它涵盖了相机镜头的显式动态模拟&#xff0c;以及对光学性能的影响。使用Ansys Mechanical和LS-DYNA对相机在地板上的一系列冲击和弹跳过程…...

工具收集 - java-decompiler / jd-gui

工具收集 - java-decompiler / jd-gui 参考资料 用法&#xff1a;拖进来就行了 参考资料 https://github.com/java-decompiler/jd-gui 脚本之家&#xff1a;java反编译工具jd-gui使用详解...

《无线重构世界》射频模组演进

射频前端四大金刚 射频前端由PA、LNA、滤波器、开关“四大金刚” 不同的模块有自己的工艺和性能特点 分层设计 射频前端虽然只由PA、LNA、开关、混频器4个模块构成&#xff0c;但不同模块之间相互连接且相互影响。如果将射频系统当成一个整体来理解&#xff0c;其中的细节和…...

渗透测试---docker容器

声明&#xff1a;学习素材来自b站up【泷羽Sec】&#xff0c;侵删&#xff0c;若阅读过程中有相关方面的不足&#xff0c;还请指正&#xff0c;本文只做相关技术分享,切莫从事违法等相关行为&#xff0c;本人一律不承担一切后果 目录 一、Docker的作用与优势 二、docker的核心…...

【go从零单排】Atomic Counters原子计数

&#x1f308;Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 &#x1f4d7;概念 在 Go 语言中&#xff0c;原子计数器&#xff08;Atomic Counters&#xff09;是…...

VSCode中python插件安装后无法调试

问题 VSCode中python插件安装后无法调试&#xff0c;如下&#xff0c;点击调试&#xff0c;VScode中不报错&#xff0c;也没有调试 解决方法 1、查看配置 打开所在路径 2、拷贝 将整个文件夹拷贝到vscode默认路径下 3、问题解决 再次调试&#xff0c;可以正常使用了…...

用react实现radio同时关联proform组件

实现&#xff1a; <ProFormRadio.GroupradioType{button}name{[bodyConfig, format]}label"请求体格式"initialValue{json}options{createTabs}fieldProps{{buttonStyle: solid,wrapperMarginInlineEnd: 20,onChange: e > {let v e.target.value;databaseMod…...

Objective-C 1.0和2.0有什么区别?

Objective-C ObjC比较小众&#xff0c;在1980年左右由Stepstone公司的Brad Cox和Tom Love发明。后来NeXT公司获得ObjC语言使用权&#xff0c;再后来到1996年NeXT被苹果公司收购也变成苹果公司使用&#xff0c;Mac市场占有率本身就不高&#xff0c;ObjC没有太多程序员。在移动互…...

TCP连接如何保障数据传输安全

TCP连接保障数据传输安全主要通过以下几个机制实现&#xff1a; 1、建立连接&#xff1a;TCP通过三次握手过程建立连接&#xff0c;确保通信双方实体真实存在&#xff0c;为后续的可靠数据传输打下基础。 2、序号机制&#xff1a;TCP为每个传输的数据包分配一个序号&#xff…...

【论文复现】ChatGPT多模态命名实体识别

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀ChatGPT ChatGPT辅助细化知识增强&#xff01;1. 研究背景2. 模型结构和代码3. 任务流程第一阶段&#xff1a;辅助精炼知识启发式生成第二阶段…...

管理 Elasticsearch 变得更容易了,非常容易!

作者&#xff1a;来自 Elastic Ken Exner Elasticsearch 用户&#xff0c;我们听到了你的心声。管理 Elasticsearch 有时会变得很复杂&#xff0c;面临的挑战包括性能调整、问题检测和资源优化。我们一直致力于简化你的体验。今天&#xff0c;我们宣布了自收购 Opster 以来的一…...

SynchronousQueueworkQueue.offer() 和 poll() 方法的超时控制及线程池的讨论

2024.11.12 11:26 问&#xff1a;workQueue.offer(command) 这里 workQueue.poll(keepAlive, TimeUnit.NANOSECONDS) : 这里的 workQueue.offer(command) 没有超时时间控制&#xff1f; Claude 3.5 Haiku 非常好的问题&#xff01;我来详细解释 offer() 和 poll() 方法的区别…...

全国城建中心官网入口/百度seo关键词优化市场

Hbase支持的Hadoop版本http://hbase.apache.org/book/configuration.html#hadoop 解压文件后&#xff0c;配置conf目录下的3个文件&#xff1a; hbase-env.sh&#xff08;设定JAVA_HOME&#xff09; regionserver&#xff08;将slaves相应的IP加入&#xff09; hbase-site.xml …...

电商平台网站设计公司/北京seo公司华网白帽

Linux用户操作记录我们都可以通过命令history来查看历史记录&#xff0c;但是如果因为某人误操作了删除了重要的数据&#xff0c;那么Linux history命令就基本上不会有太大的作用了。我们怎么来查看Linux用户操作记录&#xff0c;有没有什么办法实现通过记录登陆后的IP地址和某…...

小学校园门户网站建设方案/百度竞价推广登录

1005&#xff1a;创建表失败1006&#xff1a;创建数据库失败1007&#xff1a;数据库已存在&#xff0c;创建数据库失败1008&#xff1a;数据库不存在&#xff0c;删除数据库失败1009&#xff1a;不能删除数据库文件导致删除数据库失败1010&#xff1a;不能删除数据目录导致删除…...

一个网页的制作流程/陕西优化疫情防控措施

java中集合的区别是什么&#xff1f;在java中集合主要分为&#xff1a;List&#xff0c;Set&#xff0c;Map三种&#xff0c;其中List与Set是继承自Collection&#xff0c;而Map不是。List与Set的区别&#xff1a;List中的元素有存放顺序&#xff0c;并且可以存放重复元素&…...

定制网站建设加盟代理/建站之星

LiulishuoPreview 项目地址&#xff1a;LiulishuoPreview简介&#xff1a;手摸手带你用 VideoView 实现英语流利说炫酷引导页Imitate Liulishuo guide view. Demo...

python+视频播放网站开发/宁波网站优化

mysql多实例部署软件安装//安装wget工具[rootlocalhost ~]# yum -y install wget//下载二进制格式的mysql软件包[rootlocalhost ~]# wget https://downloads.mysql.com/archives/get/p/23/file/mysql-5.7.31-linux-glibc2.12-x86_64.tar.gz使用xftp把包传进来配置用户和组并解压…...