C++:AVL树

 

概念:

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查 找元素相当于在顺序表中搜索元素,效率低下。

如图所示,搜索二叉树不能面对右边的树,这种极端的情况,这是就需要AVL树 

什么是AVL树: 

  • 当向二叉搜索树中插入新结点后,如果能保证每个结点的左右 子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均 搜索长度。
  • 一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树: 1、它的左右子树都是AVL树 2、左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

 

图上的平衡因子是右子树减去左子树的得到的,请注意无论是左子树减去右子树,还是右子树减去左子树,高度差/平衡因子都只能是 -1 、0、1这三个数之一

 AVL树的插入规则:

 如图上图左边子树的插入情况:

  • 2插入了一个新节点,这个节点在2的右边,所以2的平衡因子++ 变成1
  • 然后2的父节点1的平衡因子因为2是1的在右边,所以1的平衡因子也变++ 变成了1
  • 然后1的父节点3的平衡因子因为1是在3的左边,所以3的平衡因子变-- 变成了-2 这时候因为不算平衡因子范围内的树,平衡被打破,需要进行旋转处理

KV结构的AVL树节点代码结构:

  • _parent是父亲节点的意思,需要一个父亲节点才方便更新父亲以及祖先的平衡因子
  • kv是一个具有两个数值的东西,所以kv内部还有东西,kv的frist表示插入的顺序,而kv的second表示插入节点的数值
  • kv模型是按照kv内部的frist进行排序的
  • bf是平衡因子,平衡因子一开始就是0

插入操作:

_parent 父节点的操作:

  • 这段代码中 while找到了插入的位置后,就进行了插入操作,注意while的cur是查询插入位置的,是从根节点出发的
  • 而第二个cur是表示新插入的节点,在之前的while中,我们找到了插入位置的父节点,所以只要比较父节点的k.first是否是大于还是小于需要插入节点的kv.first
  • 随后插入之后,新节点的父节点 _parent 就变成了之前parent,这是为了方便寻找父节点的记录操作,让每一个节点内部都有连向父节点的指针。

更新平衡因子操作:

是根据parent来进行更新的,当parent为空表示平衡因子更新到了根节点

  • 这一段是查看当前节点是父节点的左节点还是右节点,对于插入的节点来说就是查看是否是更新++还是更新--
  • 而对于插入节点的父节点或者祖先节点来说,这种操作就是查看更新平衡因子后的子节点是自己的左节点还是右节点,左边就-- 右边就++
  • 如果父节点的平衡因子是1或者-1就必须还得往上面进行更新,如果是0就不需要更新了 

需要进行旋转的情况:

旋转方式: 
 1、新插入的节点使得左子树变高了

如上图所示,对于左子树更高,我们需要进行右单旋操作:

  • 让30的右边变成60的左边,同时把30的右边变成60以及他的子树,这里要准寻搜索树的规则
  • 如图所示 30比60小,所以在60的左边,同时b比30大但是比60小,所以b放在60的左边没有问题,而旋转后,60比30大,所以60在30的右边是成立的

可以看到 只动了30、 60 、b节点,所以在编写代码的时候以parent为中心进行编写 

又如上图代码所示,如果需要把30、60 b的位置进行交换,而 b 是subLR ,30是subL ,60 是parent ,在交换的过程中需要酱它们节点内部的parent 指向父亲节点的指向进行修改,同时我们需要注意 b 这个节点可能是不存在的,理由如下

所以需要判断b节点是否存在,如果存在那么b节点的内部 中的 父亲节点指针就需要进行 指向的变动,同时还需要注意以下情况:

parent 是根节点,如果parent是根节点,那么subL就需要变成根节点,同时原先的根节点 内部 的 父亲节点指针就需要指向空,同时如果parent不是根节点,就得需要找到parent的父亲节点 让他来改变指向:

如果ppnode的左节点是parent 那么的左边就改变方向指向subL,如果右节点是parent 那就右边指针改变方向指向subL,同时subL的父节点指向ppnode节点,最后因为旋转节点所以平衡因子都需要变成0以此表示平衡了:

完整代码:

2、新插入的节点使得右子树变高了 

如图所示 30 是parent subR是60 subRL是b ,如上图的操作所示,要把 subRL变成parent 的右节点 ,把parent变成subR的左节点,操作和右旋转的操作一样:

左右单旋的情况区分: 

双旋转: 

如上图所示,不论是左单旋还是右旋都无法彻底的解决问题,反而是变成了一种左右单旋来回拉扯的循环问题。

 解决方案:

 先把右子树自己内部进行右单旋,在把整体进行左单选达到最后的效果

完整代码: 

 

 未完待续............................


 

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

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

相关文章

【面试干货】http请求报文的组成与作用?

【面试干货】http请求报文的组成与作用? 一、http 的请求报文组成二、请求行(Request Line)三、请求头部(Request Headers)四、请求体(Request Body)五、响应头部 (Response Headers…

DBdoctor产品介绍

基本信息 DBdoctor是聚好看科技股份有限公司自主研发的一款数据库内核级性能诊断工具,首次将eBPF技术聚焦在了数据库领域,一分钟内定位数据库性能问题并给出优化建议,实现数据库性能诊断百倍提效。 免费下载 请在PC端打开以下链接&#x…

通过AOP实现项目中业务服务降级功能

最近项目中需要增强系统的可靠性,比如某远程服务宕机或者网络抖动引起服务不可用,需要从本地或者其它地方获取业务数据,保证业务的连续稳定性等等。这里简单记录下业务实现,主要我们项目中调用远程接口失败时,需要从本…

JavaScript异步编程——04-同源和跨域

同源和跨域 同源 同源策略是浏览器的一种安全策略,所谓同源是指,域名,协议,端口完全相同。 跨域问题的解决方案 从我自己的网站访问别人网站的内容,就叫跨域。 出于安全性考虑,浏览器不允许ajax跨域获取…

泛型编程四:容器

文章目录 前言一、序列容器verctor 总结 前言 STL有六大部件,容器、算法、仿函数、迭代器、适配器和分配器。除了算法是函数模板,其他都是类模板。容器可以分为序列容器和关联容器。常见的序列容器有vector、array、deque、list、forward-list&#xff…

微信小程序开发-数据事件绑定

🐳简介 数据绑定 数据绑定是一种将小程序中的数据与页面元素关联起来的技术,使得当数据变化时,页面元素能够自动更新。这通常使用特定的语法(如双大括号 {{ }})来实现,以便在页面上展示动态数据。 事件绑…

js如何控制一次只加载一张图片,加载完成后再加载下一张

公众号:程序员白特,欢迎一起交流学习~ 原文:https://juejin.cn/post/7340167256267391012 今天看到一个面试题,是关于img图片加载方面的,有必要记录一下。其实关于这个问题,只要知道图片什么时候加载完成就…

关于行进线路。

https://map.tianditu.gov.cn/ 作者:Chockhugh 链接:https://www.zhihu.com/question/20545559/answer/494685117 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 以50km,几乎全是…

商务分析方法与工具(五):Python的趣味快捷-文件和文件夹操作自动化

Tips:"分享是快乐的源泉💧,在我的博客里,不仅有知识的海洋🌊,还有满满的正能量加持💪,快来和我一起分享这份快乐吧😊! 喜欢我的博客的话,记得…

TMS320F2812DSP最小系统原理图及PCB文件

目录 1、原理图 2、PCB 资料下载地址:TMS320F2812DSP最小系统原理图及PCB文件 1、原理图 2、PCB

2024年第六届世界软件工程研讨会(WSSE 2024)即将召开!

2024年第六届世界软件工程研讨会(WSSE 2024)将于2024年9月13-15日在日本京都举行。软件工程领域的发展离不开各位专家学者和业界精英的共同努力和贡献。WSSE 2024将就软件工程领域的最新研究成果、实践经验和发展趋势进行深入交流和探讨,汇聚…

牛客NC97 字符串出现次数的TopK问题【中等 哈希+优先级队列 Java/Go】

题目 题目链接: https://www.nowcoder.com/practice/fd711bdfa0e840b381d7e1b82183b3ee 核心 哈希,优先级队列Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返…