【C++ 高阶数据结构 Test】AVL ~ 二叉搜索树

文章目录

      • 1. AVL 树概念
      • 2. AVL 树节点的定义
      • 3. AVL树的插入
      • 4. AVL树的旋转
        • 4.1 新节点插入较高左子树的左侧---左左:右单旋
        • 4.2 新节点插入较高右子树的右侧---右右:左单旋
        • 4.3 新节点插入较高左子树的右侧---左右:先左单旋再右单旋
        • 4.4 新节点插入较高右子树的左侧---右左:先右单旋再左单旋
      • 5. AVL树的性能
      • 6. AVL树的面试题

1. AVL 树概念

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

🍎② 因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis (为什么叫 AVL 树呢 ? ----- 是从这两位科学家的姓名来的) 在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

🍎③ 一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

  • Ⅰ、它的左右子树都是AVL树;
    Ⅱ、左右子树高度之差(简称平衡因子)的绝对值不超过1 (即:-1、0、1);

注意:
🐧a. 平衡因子不是 AVL树必须需要的,它只是 AVL 树的一种实现方式,平衡因子不是必须要维护的,在操作时也可以直接通过高度函数来算,只不过比较麻烦;

🐧b. 平衡因子 = 右子树的高度 - 左子树的高度。

在这里插入图片描述

结论: 如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 O ( l o g 2 n ) O(log_2 n) O(log2n) ,搜索时间复杂度O( l o g 2 n log_2 n log2n)。(注意,当 n = 3亿 的时候,二叉树的高度还不到 30,所以极大的提高了搜索效率)


2. AVL 树节点的定义

template<class K, class V>
struct AVLTreeNode {AVLTreeNode<K, V>* _left;AVLTreeNode<K, V>* _right;AVLTreeNode<K, V>* _parent;		// AVL树要定义节点的父亲,因为更新需要更新祖先的平衡因子pair<K, V> _kv;int _bf;	// 该节点的平衡因子//构造函数,以便初始化AVLTreeNode(const pair<K, V>& kv): _left(nullptr), _right(nullptr), _parent(nullptr), _kv(kv), _bf(0);	{}
};

3. AVL树的插入

🍎① AVL 树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么
AVL树的插入过程可以分为两步:

🐧Ⅰ. 按照二叉搜索树的方式插入新节点
🐧Ⅱ. 调整插入节点以及该节点的祖先节点的平衡因子

a. 插入父节点的左边,父节点的平衡因子 -1

b. 插入父节点的右边,父节点的平衡因子 +1

c. 父亲的平衡因子 == 0的时候,表示父亲所在子树的高度不变,不再需要往上更新,插入结束

d. 父亲平衡因子 == 1 or -1,父亲所在子树高度变了,继续往上更新;

e. 父亲平衡因子== 2 or -2,父亲所在的子树已经不平衡了,需要旋转处理

注意: ❗更新平衡因子的结束条件,要么是当前更新的父亲平衡因子等于0,要么是当前更新节点是根节点,根节点的父亲的平衡因子是不存在的(因为此时父亲节点为空),即为结束条件。

在这里插入图片描述

4. AVL树的旋转

🍎 如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,AVL树的旋转分为四种:



4.1 新节点插入较高左子树的左侧—左左:右单旋

🍎 ①右单旋:必须要 单纯的满足都是左子树比右子树高的情况 不能出现右子树比左子树高的情况

🍎 ②什么情况下使用右单旋呢 ?

在这里插入图片描述

在这里插入图片描述

	//右单旋void RotateR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;// 先稳定老大parent->_left = subLR;// 认新主人if (subLR)subLR->_parent = parent;// 将老大变成老二subL->_right = parent;// 因为 parent 可能是还有父节点的情况的Node* ppNode = parent->_parent;parent->_parent = subL;if (parent == _root){_root = subL;subL->_parent = nullptr;}else{//判断 parent 是 ppNode的左孩子还是右孩子if (ppNode->_left == parent){ppNode->_left = subL;}else{ppNode->_right = subL;}subL->_parent = ppNode;}// 将其平衡因子改变parent->_bf = subL->_bf = 0;}

4.2 新节点插入较高右子树的右侧—右右:左单旋

🍎 ① 什么情况下使用左单旋呢 ?

在这里插入图片描述


🍎 ② 注意:❗下面这种情况就不是左单旋,因为它不是单纯的右边高(它有左边高的情况

在这里插入图片描述


  • 以下是左单旋的例子:

在这里插入图片描述

	//左单旋void RotateL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;// 先稳定老大,给他派士兵parent->_right = subRL;if (subRL != nullptr)subRL->_parent = parent;// 老大变成老二subR->_left = parent;parent->_parent = subR;Node* ppNode = parent->_parent;if (parent == _root){_root = subR;subR->_parent = nullptr;}else{// 判断 parent 节点是在 ppNode的右节点还是左节点if (ppNode->_right == parent){ppNode->_right = subR;}else{ppNode->_left = subR;}subR->_parent = ppNode;}parent->_bf = subR->_bf = 0;}
4.3 新节点插入较高左子树的右侧—左右:先左单旋再右单旋

🍎 ① 大概的思路是将该二叉树变成满足完全右单旋的情况。

🍎 ② 什么情况下使用左右双旋呢 ?

在这里插入图片描述

在这里插入图片描述

// 先进行左单旋,再右单旋
void RotateLR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;// 旋转之前,保存subLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节点的平衡因子int bf = subLR->_bf;// 先对30进行左单旋RotateL(parent->_left);//  再对90进行右单旋RotateR(parent);// // 旋转之前,60的平衡因子可能是-1/0/1,旋转完成之后,根据情况对其他节点的平衡因子进行调整if (bf == -1){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 1;}else if (bf == 1){subLR->_bf = 0;subL->_bf = -1;parent->_bf = 0;}else if (bf == 0){subLR->_bf = 0;subL->_bf = 0;parent->_bf = 0;}else{assert(false);}
}
4.4 新节点插入较高右子树的左侧—右左:先右单旋再左单旋

🍎 ① 什么情况下使用右左双旋呢 ?

在这里插入图片描述

在这里插入图片描述

// 右左单旋
void RotateRL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(subR);RotateL(parent);subRL->_bf = 0;if (bf == 1){subR->_bf = 0;parent->_bf = -1;}else if (bf == -1){parent->_bf = 0;subR->_bf = 1;}else{parent->_bf = 0;subR->_bf = 0;}
}

5. AVL树的性能

🐧🐧🐧 AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即 l o g 2 ( N ) log_2 (N) log2(N)

🍎🍎🍎 但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入(插入的时候 new 出节点也很消耗时间)时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。

因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变,意思就是不会插入新数据),可以考虑AVL树,但一个结构经常修改,就不太适合。

6. AVL树的面试题

  • 🍎① AVL树插入或者删除的时候,其旋转情况?

🐧Ⅰ、插入时,AVL树最多只需要旋转两次。

🐧Ⅱ、删除操作时,可能不止旋转两次,可能需要旋转多次,子树旋转后,其高度降低了一层,其上层可能也需要跟着旋转。

在这里插入图片描述

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

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

相关文章

【JAVA入门】Day05 - 面向对象

【JAVA入门】Day05 - 面向对象 文章目录 【JAVA入门】Day05 - 面向对象一、对象的设计和使用1.1 类和对象1.2 类的分类 二、封装三、private 关键字四、this 关键字五、构造方法六、JavaBean七、对象的内存图7.1 一个对象的内存图7.2 两个对象的内存图7.3 两个引用指向同一个对…

【练习】分治--快排思想

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f525;个人专栏&#xff1a;算法(Java)&#x1f4d5;格言&#xff1a;吾愚多不敏&#xff0c;而愿加学欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 颜色分类 题目描述 题解 代码实现 排序数组 题目描述 题解 代码…

战网国际服下载教程 暴雪战网客户端一键下载安装教程分享

战网国际服务平台&#xff0c;又名Battle.net环球版&#xff0c;是暴雪娱乐操作的跨国界游戏交流平台&#xff0c;它消除了地域的隔阂&#xff0c;向全球范围内的游戏爱好者提供服务。与仅服务于特定地区的版本不同&#xff0c;国际版赋予了玩家自由穿梭于暴雪众多标志性游戏的…

ubuntu中如何删除常规匹配不到的乱码目录文件

原因是之前误操作创建了多个带空格的gerrit仓库的时候导致的服务器乱码&#xff0c;进入geriit服务器可以查看到如下的一个异常目录&#xff0c;常规rm -rf 操作的时候是匹配不到这个目录的。 这时候我们应该考虑使用inode的性质来匹配删除。 注&#xff1a;在Linux文件系统中…

数论专题练习

质数专题 我的思路就是一个素数筛&#xff0c;然后双指针 class Solution { public:int maximumPrimeDifference(vector<int>& nums) {unordered_map<int, int> mp;for (int i 2; i < 100; i) {if (mp[i] 0) {for (int j 2 * i; j < 100; j i) {mp[…

失业焦虑如何缓解心情?流静冥想

失业焦虑如何缓解心情&#xff1f;人生旅途&#xff0c;失业犹如山重水复&#xff0c;焦虑似迷雾遮望眼。古语云&#xff1a;“山不厌高&#xff0c;海不厌深。”心之向往&#xff0c;冥想便是那披荆斩棘之斧&#xff0c;如何带你走出困境&#xff1f; “静以修身”&#xff0c…

Python使用asyncio包实现异步编程

1. 异步编程 异步编程是一种编程范式&#xff0c;用于处理程序中需要等待异步操作完成后才能继续执行的情况。异步编程允许程序在执行耗时的操作时不被阻塞&#xff0c;而是在等待操作完成时继续执行其他任务。这对于处理诸如文件 I/O、网络请求、定时器等需要等待的操作非常有…

RALL-E: Robust Codec Language Modeling with Chain-of-Thought Prompting for TTS

demo pageDetai Xin&#xff0c; tanxu微软 & 东大 & 浙大 abstract 使用CoT的思路&#xff0c;和Valle的框架&#xff0c;先实现LLM预测音素级别pitch/duration&#xff0c;然后预测speech token。 methods Prosody tokens as chain-of-thought prompts 和Valle一…

6. 网络编程-网络io与select、poll,epoll

https://0voice.com/uiwebsite/html/courses/v13.7.html 首先看看这个学习计划 网络、网络编程、网络原理基础组件&#xff0c;20个。中间件 Redis ,MySQL&#xff0c;Kafka&#xff0c;RPC&#xff0c;Nginx开源框架&#xff08;解决方案&#xff09;业务开发(工程师开发&am…

(1)双指针算法介绍与练习:移动零

目录 双指针算法介绍 练习&#xff1a;移动零 双指针算法介绍 双指针算法常见于数组和双向链表的题型 在数组中&#xff0c;双指针中的指针代表数组元素的下标&#xff0c;而不是真正的指针类型变量 在双向链表中&#xff0c;双指针中的指针即为真正意义上的指针&#xff…

CCF PTA 2022年11月C++学生会提名

【问题描述】 学生会选举要开始了。根据选举规则&#xff0c;首先由全体同学进行提名&#xff0c;每位同学可以从全体同学中提 名一名同学参选。选举时&#xff0c;会从全体同学的提名中选出一名学生会主席&#xff0c;再从三个年级分别的提名中 各选出一名副主席。现在&#…

【数据结构】堆(超详细)

文章目录 前言堆的概念及结构堆的实现堆的向下调整算法&#xff08;建小堆为例&#xff09;堆的向上调整算法&#xff08;建小堆为例&#xff09;堆的初始化销毁堆堆的插入堆的删除(规定删堆顶的数据)取堆顶元素判断堆是否为空获取堆的个数 完整代码&#xff08;包括测试代码&a…

惠普发布全新AI战略,重塑办公空间 引领企业智能化新浪潮

近日、全球知名科技公司惠普在北京隆重举办了以“用智能&#xff0c;开启无限可能”为主题的2024惠普商用AI战略暨AI PC新品发布会&#xff0c;此次盛会标志着惠普在人工智能领域迈出了重要一步&#xff0c;惠普紧跟时代步伐&#xff0c;推出了更高效、更安全、更灵活的AI PC产…

大佬复活,暴打空头,两天拉升 180%

GME 暴打空头 大家还记得 2021 年&#xff0c;美国散户大战华尔街的新闻吗&#xff1f; 当时在推特上&#xff0c;几位大佬进行号召&#xff0c;吸引了大量散户往里冲&#xff0c;短短一个月&#xff0c;把一家业绩平平的美股公司「游戏驿站&#xff08;GME&#xff09;」拉升了…

怎么3d立面有些模型不能删除是什么原因怎么解决?---模大狮模型网

在进行3D建模和设计过程中&#xff0c;有时会遇到一些模型无法删除的情况&#xff0c;这可能会导致设计流程受阻&#xff0c;影响工作效率。本文将介绍在3D立面中遇到无法删除模型的原因以及解决方法&#xff0c;帮助您顺利解决这一问题&#xff0c;提高设计效率。 一、模型未正…

地平线X3开发板配置wifi调试

1. 系统镜像制作 系统镜像的制作依赖bsp与补丁包&#xff0c;bsp在天工开物全量包中&#xff1a;https://developer.horizon.ai/resource 补丁下载链接&#xff1a;链接&#xff1a;https://pan.baidu.com/s/1YKcOWL0EpboGq-SnqwIGeQ 提取码&#xff1a;b6lf 补丁包中有详细…

这 10 个 Linux 命令,我不允许你还不知道!

Linux当中有很多比较有趣的命令&#xff0c;可以动手看看&#xff0c;很简单的。 1.rev命令 一行接一行地颠倒所输入的字符串。 运行&#xff1a; $rev 如输入&#xff1a;shiyanlou shiyanlou 2.asciiview命令 1.先安装aview $sudo apt-get install aview 2.再安装im…

【吃透Java手写】6-Netty-NIO-BIO-简易版

Netty 1 BIO&NIO模型 1.1 BIO 在JDK1.4出来之前&#xff0c;我们建立网络连接的时候采用BIO模式&#xff0c;需要先在服务端启动一个ServerSocket&#xff0c;然后在客户端启动Socket来对服务端进行通信&#xff0c;默认情况下服务端需要对每个请求建立一堆线程等待请求&…

TikTok Shop认知课 打通TK小店全流程

资料 001-先导课.mp4 002-如何用思维导图工具做课程笔记.mp4 003-TTS入驻模式.mp4 004-如何获取店铺.mp4 005-TTS店铺注册全流程,mp4 006-店铺整体运营思路.mp4 007-运营的几个误区.mp4 008-新店起店准备工作,mp4 009-规店铺风控注意事项,mp4 010-店铺基础设置之店铺…

基于火山引擎云搜索的混合搜索实战

在搜索应用中&#xff0c;传统的 Keyword Search 一直是主要的搜索方法&#xff0c;它适合精确匹配查询的场景&#xff0c;能够提供低延迟和良好的结果可解释性&#xff0c;但是 Keyword Search 并没有考虑上下文信息&#xff0c;可能产生不相关的结果。最近几年&#xff0c;基…