【C++模拟实现】手撕AVL树
【C++模拟实现】手撕AVL树
目录
- 【C++模拟实现】手撕AVL树
- AVL树的介绍(百度百科)
- AVL树insert函数的实现代码
- 验证是否为AVL树
- AVL树模拟实现的要点
- 易忘点
- AVL树的旋转思路
作者:爱写代码的刚子
时间:2023.9.10
前言:本篇博客将会介绍AVL树的模拟实现(模拟AVL树的插入),以及如何去验证是否为AVL树
AVL树的介绍(百度百科)
AVL树本质上还是一棵二叉搜索树,它的特点是:
-
本身首先是一棵二叉搜索树。
-
带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。
也就是说,AVL树,本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树)。
AVL树insert函数的实现代码
template<class K,class V>
class AVLTreeNode
{
public:AVLTreeNode(const pair<K,V>& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_kv(kv),_bf(0){}AVLTreeNode* _left;AVLTreeNode* _right;AVLTreeNode* _parent;//需要设立父节点指针pair<K,V> _kv;int _bf;
};template<class K,class V>
class AVLTree
{typedef AVLTreeNode<K,V> Node;
public:AVLTree():_root(nullptr){}bool insert(const pair<K,V>& kv){if(_root==nullptr){_root=new Node(kv);return true;}else{Node* cur=_root;Node* parent=nullptr;//设计parent指针是必要的while(cur){if(cur->_kv.first>kv.first){parent=cur;cur=cur->_left;}else if(cur->_kv.first<kv.first){parent=cur;cur=cur->_right;}else{return false;}}cur=new Node(kv);//判断新加入的节点是父节点的左子树还是右子树if(parent->_kv.first>kv.first){parent->_left=cur;}else{parent->_right=cur;}cur->_parent=parent;while(parent){//及时调整父节点的平衡因子if(parent->_left==cur){--parent->_bf;}else{++parent->_bf;}if(parent->_bf==0)//当父节点的平衡因子为0时停止调整{break;}else if(parent->_bf==-1||parent->_bf==1){cur=parent;parent=parent->_parent;}else if(parent->_bf==2||parent->_bf==-2)//处理异常情况{//出现问题的情况if(parent->_bf==-2&&cur->_bf==-1){_RotateR(parent);//右单旋}else if(parent->_bf==2&&cur->_bf==1){_RotateL(parent);//左单旋}else if(parent->_bf==-2&&cur->_bf==1){ _RotateLR(parent);//左右双旋}else if(parent->_bf==2&&cur->_bf==-1){_RotateRL(parent);//右左双旋}else{assert(false);}break;}else{assert(false);}}}return true;} void _RotateR(Node* parent)//右单旋的实现{Node*cur=parent->_left;Node*curRight=cur->_right;Node*ppnode=parent->_parent;cur->_right=parent;parent->_left=curRight;if(curRight)//curRight可能是nullptr{curRight->_parent=parent;}parent->_parent=cur;//处理ppnodeif(parent==_root)//parent为头节点时需要单独处理{_root=cur;cur->_parent=nullptr;}else{if(ppnode->_left==parent){ppnode->_left=cur;}else{ppnode->_right=cur;}cur->_parent=ppnode;}parent->_bf=cur->_bf=0;}void _RotateL(Node* parent){Node* cur=parent->_right;Node* curLeft=cur->_left;Node* ppnode=parent->_parent;cur->_left=parent;parent->_right=curLeft;if(curLeft){curLeft->_parent=cur;}parent->_parent=cur;if(parent==_root){_root=cur;cur->_parent=nullptr;}else{if(ppnode->_left==parent){ppnode->_left=cur;}else{ppnode->_right=cur;}cur->_parent=ppnode;}parent->_bf=cur->_bf=0;}void _RotateLR(Node* parent){Node* cur=parent->_left;Node* curRight=cur->_right;int bf=curRight->_bf;_RotateL(cur);_RotateR(parent);//最好再处理一下平衡因子,减少耦合度if(bf==0)//单链情况下{parent->_bf=0;cur->_bf=0;curRight->_bf=0;}else if(bf==-1){parent->_bf=1;curRight->_bf=0;cur->_bf=0;}else if(bf==1){parent->_bf=-1;curRight->_bf=0;cur->_bf=0;}else{assert(false);}}void _RotateRL(Node* parent){Node* cur=parent->_right;Node* curLeft=cur->_left;int bf=curLeft->_bf;_RotateR(cur);_RotateL(parent);if(bf==0){parent->_bf=0;curLeft->_bf=0;cur->_bf=0;}else if(bf==1){parent->_bf=0;curLeft->_bf=-1;cur->_bf=0;}else if(bf==-1){parent->_bf=0;curLeft->_bf=1;cur->_bf=0;}else{assert(false);}}private:Node* _root;
};
验证是否为AVL树
int _Height(Node* root){if(root==nullptr){return 0;}int leftHeight=_Height(root->_left);int rightHeight=_Height(root->_right);return leftHeight>rightHeight?leftHeight+1:rightHeight+1;}bool _Isbalance(){return _Isbalance(_root);}bool _Isbalance(Node* root){if(root==nullptr){return true;}int right=_Height(root->_right);int left=_Height(root->_left);if(root->_bf!=right-left){cout<<"平衡因子异常"<<root->_bf<<" "<<right<<" "<<left<<endl;return false;}return abs(right-left)<2&&_Isbalance(root->_left)&&_Isbalance(root->_right);}
-
根据AVL树的特性引入两个成员函数_Height函数用于计算二叉树的高度
-
以下为验证结果:
AVL树模拟实现的要点
易忘点
一定要时刻注意_parent指针的修改!尤其旋转函数中需要判断旋转后的二叉树的根节点是否还有父亲节点,如果有,需要在旋转前先保存,之后再链接上。
AVL树的旋转思路
- 新增在左,parent平衡因子减减
- 新增在右,parent平衡因子加加
- 更新后parent平衡因子 == 0,说明parent所在的子树的高度不变,不会再影响祖先,不用再继续沿着到eot的路径往上更新
- 更新后parent平衡因子 == 1 0r -1,说明parent所在的子树的高度变化,会再影响祖先,需要继续沿着到root的路径往上更新更新后
- 更新后parent平衡因子 == 2 or -2,说明parent所在的子树的高度变化且不平衡,对parent所在子树进行旋转,让他平衡
- 更到根节点,插入结束
由于AVL树画图较为麻烦,作者先不画了,可以看看其他大佬的博客,一些需要注意的地方已经写在代码注释里了,AVL树的删除之后有机会可以模拟实现一下。
AVL树的调试较为麻烦,模拟实现可以提高自己的调试能力。
相关文章:
【C++模拟实现】手撕AVL树
【C模拟实现】手撕AVL树 目录 【C模拟实现】手撕AVL树AVL树的介绍(百度百科)AVL树insert函数的实现代码验证是否为AVL树AVL树模拟实现的要点易忘点AVL树的旋转思路 作者:爱写代码的刚子 时间:2023.9.10 前言:本篇博客将…...
如何重置 docker中的mariadb的root
停止 Mariadb 容器:运行以下命令停止正在运行的 Mariadb 容器: docker stop <container_name>将 <container_name> 替换为你的 Mariadb 容器的名称或容器ID。 删除 Mariadb 容器:运行以下命令删除已停止的 Mariadb 容器&#x…...
设计模式系列-原型模式
一、上篇回顾 上篇创建者模式中,我们主要讲述了创建者的几类实现方案,和创建者模式的应用的场景和特点,创建者模式适合创建复杂的对象,并且这些对象的每 个组成部分的详细创建步骤可以是动态的变化的,但是每个对象的组…...
家用电脑可以用做服务器吗
家用电脑的结构与服务器的结构是相同的,家用电脑是可以用来搭建服务器使用。但使用家用电脑做服务器在稳定性会比服务器差很多 1.家用电脑没有公网IP,网络运营商分配的IP重启路由之后是会变化,不固定。服务器运行是需要有固定IP让人连接访问。…...
CRM软件管理系统的基本功能
CRM管理系统是企业运营的重要工具,它可以帮助企业管理客户关系,提升销售效率,大幅提高客户转化率,实现业绩增长。那么,CRM管理系统一般包含哪些功能呢?下面我们就来说说。 1、销售自动化 销售自动化顾名思…...
手机喊话应用实现思路
手机要是动一下,就喊话“摇摇零线,摇摇零线”,是不是比较酷, 这里实现一下手机翻转一下,播放声音的效果, 通过sensor识别到手机的运动状况,然后播放音频, public class MainActivi…...
【ARM CoreLink 系列 3 -- CCI-550 控制器介绍 】
文章目录 CCI FamilyCCI-550 简介CCI-550 功能CCI-550 Interfaces Snoop filter 使用背景CCI-550 Snoop filter 上篇文章:ARM CoreLink 系列 2 – CCI-400 控制器简介 CCI Family CCI-550 简介 Arm CoreLink CCI-550 Cache Coherent Interconnect 扩展了 CoreLink…...
最长递增子序列 -- 动规
300. 最长递增子序列 注意「⼦序列」和「⼦串」的区别,⼦串⼀定是连续的,⽽⼦序列不⼀定是连续的。 class LengthOfLIS:"""300. 最长递增子序列https://leetcode.cn/problems/longest-increasing-subsequence/description/""&q…...
linux 进程管理命令
进程管理命令 查看进程命令 ps命令 显示系统上运行的进程列表 # 查看系统中所有正在运行的系统ps aux# 获取占用内存资源最多的10个进程,可以使用如下命令组合:ps aux|head -1;ps aux|grep -v PID|sort -rn -k 4|head# 获取占用CPU资源最多的10个进程&am…...
第一章:计算机网络和因特网
什么是因特网 具体构成描述 互联网是一个世界范围的计算机网络,即一个互联了遍及世界数十亿计算机设备的网络,这些被连接的设备被称为主机或者端系统。端系统通过通信链路(communication link)和分组交换机(packet s…...
Android后退堆栈
修改代码 现在的ItemClick使得用户单击其中一个项目时就会跳转,现在要修改其使得在一个小屏幕设备上才会这样做,在一个大屏幕设备上运行用户选择一个训练项目时在右边的片段显示响应的信息。 希望片段处理后退的方式:假设用户在手机上运行这…...
网络原理(一)网络基础,包括IP ,网络相关的定义
网络基础,包括IP ,网络相关的定义 网络基础冲突域广播域DNSNATNAPT 网络基础 以下图片是书上的网图。 什么是IP地址? IP地址(Internet Protocol Address)是指互联网协议地址,又译为网际协议地址。P地址是…...
Python语义分割与街景识别(2):环境搭建
前言 本文主要用于记录我在使用python做图像识别语义分割训练集的过程,由于在这一过程中踩坑排除BUG过多,因此也希望想做这部分内容的同学们可以少走些弯路。 本文是python语义分割与街景识别的第二篇,关于环境搭建的内容。这个部分是整个流…...
stm32(GD32,apm32),开优化后需要特别注意的地方
提到优化就不得不提及 volatile 使用场景 1:中断服务程序中修改的供其它程序检测的变量,需要加volatile; : 2:多任务环境下各任务间共享的标志,应该加volatile; 3:并行设备的硬件寄存器&#x…...
LLVM 与代码混淆技术
项目源码 什么是 LLVM LLVM 计划启动于2000年,开始由美国 UIUC 大学的 Chris Lattner 博士主持开展,后来 Apple 也加入其中。最初的目的是开发一套提供中间代码和编译基础设施的虚拟系统。 LLVM 命名最早源自于底层虚拟机(Low Level Virtu…...
R语言---使用runway进行机器学习模型性能的比较
R语言—使用runway进行机器学习模型性能的比较 #dataloadrm(list=ls())#librarylibrary(dcurves)library(gtsummary)library(tidyverse)library(mlr3verse)library(tidyverse)library(data.table)</...
C++斩题录|递归专题 | leetcode50. Pow(x, n)
个人主页:平行线也会相交 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 平行线也会相交 原创 收录于专栏【手撕算法系列专栏】【LeetCode】 🍔本专栏旨在提高自己算法能力的同时,记录一下自己的学习过程,希望…...
详解Redis之Lettuce实战
摘要 是 Redis 的一款高级 Java 客户端,已成为 SpringBoot 2.0 版本默认的 redis 客户端。Lettuce 后起之秀,不仅功能丰富,提供了很多新的功能特性,比如异步操作、响应式编程等,还解决了 Jedis 中线程不安全的问题。 …...
【3】单着色器文件读取
Basic.shader文件,可以发现顶点着色器和片段着色器是写在一个文件里的,这里我们将他们读取出来,而不是上一篇使用string的方式。 #shader vertex #version 330 corelayout(location 0) in vec4 position;void main() {gl_Position positio…...
祝贺埃文科技入选河南省工业企业数据安全技术支撑单位
近日,河南省工业信息安全产业发展联盟公布了河南省工业信息安全应急服务支撑单位和河南省工业企业数据安全技术支撑单位遴选结果,最终评选出19家单位作为第一届河南省工业信息安全应急服务支撑单位和河南省工业企业数据安全技术支撑单位。 埃文科技凭借自身技术优势…...
Chinese-LLaMA-Alpaca-2模型的测评
训练生成效果评测 Fastchat Chatbot Arena推出了模型在线对战平台,可浏览和评测模型回复质量。对战平台提供了胜率、Elo评分等评测指标,并且可以查看两两模型的对战胜率等结果。生成回复具有随机性,受解码超参、随机种子等因素影响ÿ…...
SLAM论文详解(5) — Bundle_Adjustment_LM(BALM)论文详解
目录 1 摘要 2 相关工作 3 BA公式和导数 A. 直接BA公式 B. 导数 C. 二阶近似 4 自适应体素化 5. 将BALM结合进LOAM 6. 实验 7. 算法应用场景解析 1 摘要 Bundle Adjustment是一种用于同时估计三维结构和传感器运动运动的优化算法。在视觉SLAM,三维重建等…...
C语言对单链表所有操作与一些相关面试题
目录 单链表的特性 单链表的所有操作 定义一个单链表 创建一个链表头 插入数据(头插法) 插入数据(尾插法) 查找节点 修改数据节点 删除节点 打印数据 销毁链表 翻转链表 打印链表长度 冒泡排序 快排 堆排 查找倒数第K个节点(双指针法) …...
高防服务器如何抵御大规模攻击
高防服务器如何抵御大规模攻击?高防服务器是一种专门设计用于抵御大规模攻击的服务器,具备出色的安全性和可靠性。在当今互联网时代,网络安全问题日益严重,DDOS攻击(分布式拒绝服务攻击)等高强度攻击已成为…...
Go 接口和多态
在讲解具体的接口之前,先看如下问题。 使用面向对象的方式,设计一个加减的计算器 代码如下: package mainimport "fmt"//父类,这是结构体 type Operate struct {num1 intnum2 int }//加法子类,这是结构体…...
Git忽略文件的几种方法,以及.gitignore文件的忽略规则
目录 .gitignore文件Git忽略规则以及优先级.gitignore文件忽略规则常用匹配示例: 有三种方法可以实现忽略Git中不想提交的文件。1、在Git项目中定义 .gitignore 文件(优先级最高,推荐!)2、在Git项目的设置中指定排除文…...
C语言——指针进阶(2)
继续上次的指针,想起来还有指针的内容还没有更新完,今天来补上之前的内容,上次我们讲了函数指针,并且使用它来实现一些功能,今天我们就讲一讲函数指针数组等内容,废话不多说,我们开始今天的学习…...
【汇编中的寄存器分类与不同寄存器的用途】
汇编中的寄存器分类与不同寄存器的用途 寄存器分类 在计算机体系结构中,8086CPU,寄存器可以分为以下几类: 1. 通用寄存器: 通用寄存器是用于存储数据和执行算术运算的寄存器。在 x86 架构中,这些通用寄存器通常包括…...
基于文本提示的图像目标检测与分割实践
近年来,计算机视觉取得了显着的进步,特别是在图像分割和目标检测任务方面。 最近值得注意的突破之一是分段任意模型(SAM),这是一种多功能深度学习模型,旨在有效地从图像和输入提示中预测对象掩模。 通过利用…...
【4-5章】Spark编程基础(Python版)
课程资源:(林子雨)Spark编程基础(Python版)_哔哩哔哩_bilibili 第4章 RDD编程(21节) Spark生态系统: Spark Core:底层核心(RDD编程是针对这个)Spark SQL:…...
关于公司网站怎么做/实时新闻热点
java.lang.IllegalStateException: getOutputStream() has already been called for this response 参考: https://my.oschina.net/zhongwenhao/blog/209653或者把返回值改为void...
网站建设临沂/seo短视频网页入口
<style>.animation {width: 30px;height: 30px;animation: change 5s infinite;}/*动画相比过渡可以控制更多细节,可以将一个动画拆成多个步骤*/keyframes change {0% {width: 30px;height: 30px;background-color: yellow;}/*25% {width: 300px;background-c…...
专业建站公司报价/网站关键词排名如何提升
在.net中使用ie WebBrowser控件时,需要跟据自已的需求来定制组件的事件,风格等。其实在.net里控制ie组件的操作很简单,并且可以使用MSHTML提供的所有功能。您可以截获用户的所有操作事件,可以取得用户操作的对像以及对像的属性。下…...
什么是html5网站/建设一个网站的具体步骤
据悉以AI芯片知名的谷歌由于无法承受台积电昂贵的代工价格,决定将它的第二代Tensor SoC芯片继续交给三星以4nm工艺代工生产,凸显出美国芯片已对持续大幅涨价的台积电相当不满。2020年Q4全球芯片供应短缺,汽车芯片行业最先承受不了芯片供应紧张…...
国外设计网站导航/优化排名 生客seo
使用mvc模块开发JavaFX桌面应用在JavaFX系列文章第一篇 [JavaFX桌面应用开发-HelloWorld] 已经提到过,这里单独整理使用mvc模式开发开发的流程。对于mvc模式,用struts2或springmvc开发JavaEE项目的程序员来说并不陌生,mvc模式分为control(控制…...
公司自建网站备案/青岛seo精灵
战略寻源需要一种积极主动的供应链管理方法,这一点现在可能很明显。然而,企业不能浪费宝贵的资源来持续监控和管理供应商绩效。 电子寻源有助于克服人工管理供应商关系和采购的标准障碍。战略寻源自动化已经非常流行,原因如下:改进…...