二叉搜索树(AVL树,红黑树)+封装

news2024/12/26 16:05:50

就像学习其他的东西一样,首先我们要知道二叉搜索树的作用和定义是什么!

首先顾名思义,二叉搜索树肯定是被用来为搜索服务的数据结构。

并且它的搜索效率可以达到logN,也就是一百万的数据也只用查找几十次(AVL树可以控制在20次左右,红黑树可以控制在20到40次之间)

那么接下来我们就了解它是如何实现这一神奇操作的。

首先我们给出二叉搜索树的定义。(首先知道二叉树)

二叉搜索树就是让二叉树的左节点一定小于父节点,右节点一定大于夫节点。

比如这张图就是一个平衡二叉树。

接下来我们看实现的代码。

首先是一棵树的基本组成结构左节点,右节点,还有节点的数据

首先是如果这是根节点那就直接创建然后返回就可以了。

然后如果不是根节点,那我们就要先找到正确的位置再进行插入。

那么这个时候我们可以先用一个cur指针去进行寻找。

通过值的大小选择不同的走向,一直到遇到一颗树正确的根节点的正确插入位置。(其实就是让父节点达到正确的叶节点)

然后插入。但是我们要实现链接所以,我们必须要记录一下父节点的位置,这样才能让我进行链接。

  

然后通过判断选择正确的左右方向插入即可。

找到的实现基本同理,主要也是向下寻找即可。

接下来我们来研究删除!

删除的第一步肯定还是先要找到才能删除

那么如果要找的值大于当前节点的值就找右边,反正就找左边。

如果cur找到了空节点任然没有相等的则直接跳出循环,返回false即删除失败,如果找到了

则开始删除的程序。

首先如何找到的节点如果左子树为空并且cur节点为父节点的左边,那我们就把右子树与父节点链接起来。反正就连接左子树与夫节点。当我们重新连接完树以后就可以删除节点的数据了。

当然有一种特殊情况就是如果删除的节点是根节点,那么我们就把根节点移交出去即可。

如果右子树为空是同理的。

接下来如果是左右都不为空。

那么我们显然就不能直接重新连接然后删除了。

比如下图

如果删除了10,那么无论如何连接都很难再恢复平衡。

那么接下来我们就要用新的方法-----替换法

简单来说就是用10的右子树的最左节点(最小的那个)

或者左子树最右的那个节点。

比如我们用12替换10

这个时候不仅让10消失了,而且还让树维持了平衡。

用9换10同样可以。

接下来我们就代码实现替换法。(右子树版)

首先subleft以删除节点的右子树为根节点开始向最左子树出发。

然后替换值,并且重新进行连接。这个地方我们要注意,因为最左节点可能还有右子树,那么这个时候我们可以先记录父节点,方便进行重新连接,如果右子树只有一个节点,这个时候subleft将会是parent的右节点,除此之外都会是左节点。

然后如果不是一个节点的时候那么就相当于把父节点的左节点替换成了原本左节点的右节点

比如此处就可以将50替换成51,然后将52替换成55的左节点

如果只有一个节点相当于将50替换成55,右节点直接置空。

以上就是删除的全过程,可以多理几遍。

然后就是直接销毁(后序遍历销毁),搭配析构函数使用

接下来是构造函数的实现

首先是无参构造,没有实际内容,c++11中新增关键字dafalut(表示什么都没有)就可以在这里使用。

然后是拷贝构造,利用copy函数实现

接下来就是coy函数的实现,通过遍历进行一对一复制即可

主要就是复制一个相同的节点出来就行了。

最后就是遍历,平衡二叉树的中序遍历是升序的(性质),那么基本的平衡二叉树就讲完了。

上面讲的主要是value模型,也就是直接存储值的,接下来我们补充一下另外一种模型,叫做kv模型

也就是key-value的模型。

主要就是把数据类型更改了,基本思想没有什么改变。

接下来我们就开始讲AVL树。

我们在讲AVL树之前先要知道为什么要创造AVL树,AVL树有什么作用.

我们看下面这幅图。

这也是一颗平衡二叉树,但是我们查找的效率却是n(时间复杂度看最坏情况)

那就是因为数据的不平衡导致的(数据都倾向于一边)

那么就违反了我们的初衷。

这个时候我们就要想办法将二叉树重新平衡,由此诞生了AVL树。

首先我们看AVL树的规则

那么我们就要来研究如何维持该规则。

比如下图就是一个违反规则的情况,那么我们必须调整去保证规则

首先我们可以引入平衡因子(可以不引入,但是比较麻烦),即右子树减去左子树的值

左右子树的高度差已经到达了2(+2,-2都要调整,调整方式不同),所以这个时候就必须调整了。

那么AVL树的解决方法就是旋转。

比如上图经过左旋就变成上图。

这样就符合了AVL树的定义。

总结就是

以下这种情况就要左旋

下面则是右旋的操作。

总结也就是下面这种情况要右旋

其中左旋就是将要旋转的父节点的右节点的左子树交给父节点,然后将父节点变成右节点的左子树。

右旋同理。

AVL树插入共有4种情况,上面是其中的2种。

接下来我们来介绍另外两种情况。

上面这个图显然无法通过左/右单旋进行修复。

那么我们可以用双旋进行恢复,如下图。

通过两次旋转就重新恢复了平衡。

插入b,c旋转方法是一致的。

但是如果是下面这种情况则要先左旋再右旋。

综上就是旋转的四种情况,接下来我们就要代码实现了。

首先是节点的结构,不再赘述。

然后就是研究插入。

首先还是判断根节点的特殊情况,然后开始寻找正确的位置插入,与平衡二叉树一样。

然后就是根据平衡因子判断要旋转的情况,然后进行旋转。

接下来是具体的旋转代码。

主要注意几点,第一由于父节点(除了根节点)都有父节点,所以为了正确连接,我们必须把它记录下来。

第二如果父亲是根节点则直接移交根节点,并且避免错误连接。

第三这个顺序一定要正确,必须提前判断根节点,否则parentParen->可能会报空。

由于本文章仅仅是了解AVL树和红黑树,就不深入探究AVL树和红黑树的删除了。

接下来我们来了解红黑树。

以下是它的规则。

此处注意这个地方的叶子不是我们平时说的叶子,而是最后的空节点。

通过上面的规则我们就可以保证最长路径不超过最短路径的2倍。(仍然是近似平衡,查找效率任然是logN)

首先还是写出节点的结构。(颜色可以使用枚举)

然后我们来思考如何维持红黑树。

主要分以下几种情况。

由于要尽量使得影响较小,插入新的节点都是红色节点。

第一种如果父节点是黑色,则符合规则,不用改动。

第二种,如果父节点和叔叔节点都是红色

则向上更新使得父叔节点都变为黑色,祖父节点变成红色,再以祖父节点为新的开始重复此过程。

直到更新结束,最后再让根节点变为黑色即可。

第三种则是父节点为红,叔节点为黑或者不存在。

则进行旋转加变色。

接下来就是代码实现(其中包含封装,我们稍后讲解)

仍然是先判断根节点再向下寻找正确的位置。

然后开始插入新的节点。

首先就是直接变色的情况,保证向上处理即可。

然后就是旋转的情况,先分成祖父节点的左子树和右子树,分为单旋和双旋(看插入节点是父节点的左还是右),最终都是将cur放到祖父节点,然后保持更改颜色即可。

两种情况类似,不多赘述。

最关键的是记得保证根节点为黑色!

接下来我们来讲map和set的封装。(其底层都是红黑树)

要想封装好,首先要写好迭代器。

将常用的运算符重载,尤其值得注意的就是++和--(这里只讲++)

关键点是:每次查找到了右子树最后一个节点就向上找第一个孩子是父亲左的祖先,如该图6遍历完以后就去遍历8.(中序遍历)。其余的比较简单不多赘述。

接下来我们着重讲一下封装。

首先我们要值得map和set的不同点,前者为key-value数据,后者为key数据。

所以针对这个我们就要进行部分地方的特殊处理(其余大多数地方都可以复用)。

二者虽然是同一颗树但是通过模板的数据类型不同就可以实现存储不同的数据。

其次就是对迭代器要更改。

这样就把数据类型替换了,所以我们代码里面的比较部分就需要更改了。

我们并没有直接调用数据,而是把数据传入我们的内部仿函数(结构体也可以当作类看待)

这样就实现同一棵树不同数据类型,不同数据类型不同处理方法的封装!(主要是充分复用了代码)!

 

这里要注意,由于我们要取mapkeyoft中的内嵌类型,我们不能用class,只能用typename。

然后我们写一下insert,这个也只是通过更改传入的数据类型,复用了树本身的insert。

其中又因为insert返回的pair中有迭代器,所以重载[]的时候就可以复用Insert.

综上,就是本文的全部内容,感谢观看!

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

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

相关文章

日常工作中常用的抓包工具都有哪些呢?

📢专注于分享软件测试干货内容,欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!📢交流讨论:欢迎加入我们一起学习!📢资源分享:耗时200小时精选的「软件测试」资…

电子合同的分类有哪些?

1、从电子合同订立的具体方式的角度,可分为利用电子数据交换订立的合同和利用电子邮件订立的合同; 2、从电子合同标的物的属性的角度,可分为网络服务合同、软件授权合同、需要物流配送的合同等; 3、从电子合同当事人的性质的角度…

使用Gitee中的CI/CD来完成代码的自动部署与发布(使用内网穿透把本地电脑当作服务器使用)

📚目录 📚简介:⚙️ 所需工具:💨内网穿透配置💭工具介绍✨命令安装🎊配置Cpolar🕳️关闭防火墙🥛防火墙端口放行规则(关闭防火墙可以忽略)🍬小章总…

【python】作用域与闭包 || global与nonlocal

python作用域 其他语言的作用域:块级、函数、类、模块、包等由小到大的级别但是python没有块级(if语句块、for语句块),所以if中定义的变量,相当于普通语句 >>> if True: # if语句块没有作用域x …

华为云之ECS云产品快速入门

华为云之ECS云产品快速入门 一、ECS云服务器介绍二、本次实践目标三、创建虚拟私有云VPC1.虚拟私有云VPC介绍2.进入虚拟私有云VPC管理页面3.创建虚拟私有云4.查看创建的VPC 四、创建弹性云服务器ECS——Linux1.进入ECS购买界面2.创建弹性云服务器(Linux)——基础配置步骤3.创建…

如何使用 templ 在 Go 中编写 HTML 用户界面?

关注公众号【爱发白日梦的后端】分享技术干货、读书笔记、开源项目、实战经验、高效开发工具等,您的关注将是我的更新动力! 简介 templ 是一个在 Go 中编写 HTML 用户界面的语言。使用 templ,我们可以创建可呈现 HTML 片段的组件&#xff0c…

基于改进YOLOv7的绝缘子缺陷检测算法

摘要 现有的检测方法面临着巨大的挑战,在识别绝缘子的微小缺陷时,针对输电线路图像与复杂的背景。为保证输电线路的安全运行,提出一种改进的YOLOv 7模型,以提高检测结果。 首先,基于K-means对绝缘子数据集的目标盒进…

Redis“垃圾”过期死键管理与优化

【作者】付磊 Redis死键的定义不尽相同,通常有两种: 写到Redis里后,由于过期时间过长或者压根没有过期时间,加之长期不访问,这类key可以被称为死键。 明明已经过了过期时间,但还占用Redis内存&#xff08…

利用tf-idf对特征进行提取

TF-IDF是一种文本特征提取的方法,用于评估一个词在一组文档中的重要性。 一、代码 from sklearn.feature_extraction.text import TfidfVectorizer import numpy as npdef print_tfidf_words(documents):"""打印TF-IDF矩阵中每个文档中非零值对应…

Nacos-服务发现与配置管理v1.0

Nacos - 服务发现和配置管理 教学目标 1)能够理解微服务架构的特点 2)能够理解服务发现的流程 3)能够说出Nacos的功能 4)掌握Nacos的安装方法 5)掌握RESTful服务发现开发方法 6)掌握Dubbo服务发现开…

AIGC绘画关键词 - 神兽类(一)

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC 👉关于作者 专注于Android/Unity和各种游戏开发技巧,以及各种资源分…

JNI学习(二)

静态注册 接着上篇博客学习 JNI函数 JNIEXPORT void JNICALL Java_com_example_jnidemo_TextDemo_setText(JNIEnv *env, jobject this, jstring string){ __android_log_print(ANDROID_LOG_ERROR, "test", "invoke set from C\n");char* str (char*)(*e…

每日一题——链表的回文结构

链表的回文结构 1. 题目描述 对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。 给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。 测试…

SpringBoot集成swagger-ui

1.引入依赖&#xff1a; <!--swagger--><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><version>2.7.0</version></dependency><dependency><groupId>io.sprin…

回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 (多指标,多图)

回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 &#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 &#xff08;多指标&#xff0c;多图&#…

查找算法——二分查找

笔记&#xff1a;二分查找算法 | 数据结构与算法 系列教程&#xff08;笔记&#xff09; 题目描述 请对一个 有序数组 进行二分查找 {1,8, 10, 89, 1000, 1234}&#xff0c;输入一个数看看该数组是否存在此数&#xff0c;并且求出下 标&#xff0c;如果没有就提示「没有这个数…

前端ICON库

前端ICON库 1.mingcute mingcute 2.lordicon lordicon 3.字节iconpark&#xff08;推荐&#xff09; 字节iconpark 4.iconbuddy iconbuddy.app/ 5.商标寻找youicons 免费下载数百万个徽标以获得设计灵感 | YouIcons.com 还有一堆工具

一键转换,将HTML智能转换为PDF,轻松解决文档转换需求

在数字时代&#xff0c;HTML网页是我们获取信息的主要来源之一。然而&#xff0c;有时候我们可能需要将网页内容以PDF格式保存&#xff0c;以便于离线阅读、打印或分享。这时&#xff0c;将HTML转换为PDF就变得尤为重要。 首先&#xff0c;我们要进入首助编辑高手主页面&#x…

华为数通方向HCIP-DataCom H12-831题库(多选题:181-200)

第181题 如图所示,R1、R2、R3、R4都部署为SPF区域0,链路的cost值如图中标识。R1、R2R3、R4的Loopback0通告入OSPF。R1、R2、R3与R4使用Loopback0作为连接接口,建立BGP对等体关系,其中R4为RR设备,R1、R2、R3是R4的客户端。当R4的直连地址172.20,1,4/32通告入BGP后,以下关R…

单细胞转录组揭示杨树维管组织结构与发育

之前我们有一篇PC的文章&#xff0c;专门介绍了通过单细胞转录组分析杨树茎中初生和次生生长组织中形成层细胞分化的分子轨迹&#xff08;文章回顾&#xff1a;植物单细胞转录组 | 杨树初生生长和次生生长中形成层细胞分化的分子轨迹&#xff09;。2023年发表于《Molecular Pla…