C++:探索AVL树旋转的奥秘

在这里插入图片描述

文章目录

  • 前言 AVL树为什么要旋转?
  • 一、插入一个值的大概过程
    • 1. 插入一个值的大致过程
    • 2. 平衡因子更新原则
    • 3. 旋转处理的目的
  • 二、左单旋
    • 1. 左单旋旋转方式总处理图
    • 2. 左单旋具体会遇到的情况
    • 3. 左单旋代码总结
  • 三、右单旋
    • 1. 右单旋旋转方式总处理图
    • 2. 右单旋具体会遇到的情况
    • 3. 右单旋代码总结
  • 四、双旋
    • 1. 右左双旋旋转逻辑
    • 2. 右左双旋可能会遇到的问题辨析
    • 3. 右左双旋平衡因子的处理
    • 4. 右左双旋代码总结
  • 五、左右双旋
  • 总结


前言 AVL树为什么要旋转?

AVL树需要旋转是为了保持平衡。当插入或删除节点后,某些子树的高度差超过1,就会破坏AVL树的平衡性。为了让树重新平衡,避免一边过高、一边过低的情况,旋转可以调整节点的位置,使树保持左右高度差不超过1。这样做的目的是确保查找、插入、删除操作的效率始终保持在 O(log₂ n)。简单来说,旋转就是“调位置,让树站得更稳”。


一、插入一个值的大概过程

1. 插入一个值的大致过程

  1. 按照二叉搜索树规则插入
    插入的新节点位置依据二叉搜索树的性质确定,即小于当前节点放左子树,大于当前节点放右子树。

  2. 更新平衡因子
    新增节点后,只会影响其祖先节点的高度,可能导致部分祖先节点的平衡因子发生变化。从新增节点向根节点逐步更新平衡因子。如果在更新过程中某节点的平衡因子变为2或-2,说明该节点的子树已经不平衡,需要旋转处理;否则,插入结束。

  3. 检查平衡并调整

    • 如果更新平衡因子的过程中没有发现问题(平衡因子均为0、1或-1),插入操作完成。
    • 如果出现不平衡,则对不平衡的子树进行旋转处理。旋转不仅恢复子树的平衡,还会降低子树的高度,确保不再影响其父节点的平衡因子,从而结束插入过程。

2. 平衡因子更新原则

  1. 平衡因子公式
    在这里插入图片描述

    只有子树高度发生变化时,才会影响当前节点的平衡因子。

  2. 更新规则

    • 若新增节点在父节点的右子树,则父节点的平衡因子增加1(+1)。
    • 若新增节点在父节点的左子树,则父节点的平衡因子减少1(-1)。
  3. 更新停止条件

    • 平衡因子变为0
      父节点平衡因子从 -1 变为 0 或从 1 变为 0,说明新增节点插入到高度较低的一侧,子树高度未改变,更新过程可以停止。
    • 平衡因子变为1或-1
      父节点平衡因子从 0 变为 1 或从 0 变为 -1,说明新增节点插入后子树高度增加,但仍符合平衡要求,需继续向上更新。
    • 平衡因子变为2或-2
      父节点平衡因子从 1 变为 2 或从 -1 变为 -2,说明子树高度过高,已失去平衡,需要进行旋转处理。

在这里插入图片描述

在这里插入图片描述


3. 旋转处理的目的

  1. 恢复平衡
    通过单旋转或双旋转调整节点位置,使当前子树符合平衡要求。
  2. 降低子树高度
    旋转后,子树高度恢复到插入前的水平,确保不会对更高层节点产生影响,插入过程结束。

二、左单旋

形成条件:parent->_bf == 2 && cur->_bf == 1

1. 左单旋旋转方式总处理图

  1. 失衡检测

    • 当插入的新节点导致父节点的平衡因子为 2,并且新节点被插入到右子树的右侧时,发生右右失衡(RR失衡)。
  2. 旋转操作

    • 左单旋的核心目标是将父节点的右子树(即失衡节点的右子树根)提升为新的根节点,并将原来的父节点挂接到新根节点的左子树上。
parent->right = cur->left;  // 将右子树的左子树挂接到父节点的右子树
cur->left = parent;         // 将父节点挂接为右子树的左子树
  1. 处理父节点链接问题

    • 需要确保旋转后父节点的父节点(如果存在)正确地连接到新的根节点。
      • 如果原父节点有父节点(即不是根节点),则要更新父节点的左右子树指向新的根节点。
      • 如果原父节点是根节点,则更新树的根节点。
  2. 更新平衡因子

    • 旋转后,原父节点和新的根节点的平衡因子都应设置为 0,因为旋转使得这两颗子树已经平衡。
  3. 旋转结束

    • 完成旋转后,新的根节点成为子树的根,树恢复平衡。
      在这里插入图片描述

2. 左单旋具体会遇到的情况

我们具体会遇到比如 h = 0, h = 1, h = 2 …无穷多种情况:

分析如下:

在这里插入图片描述


3. 左单旋代码总结

// 左单旋
void RotateL(Node* parent)
{Node* cur = parent->_right;Node* curleft = cur->_left;// 重新链接parent->_right = curleft;if (curleft) // 如果curleft存在{curleft->_parent = parent;}cur->_left = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (ppnode == nullptr){_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;
}

三、右单旋

形成条件:parent->_bf == -2 && cur->_bf == -1

1. 右单旋旋转方式总处理图

右单旋处理的方式与左单旋是一致的,只不过是反过来了,就不多介绍了。

  1. 失衡检测

    • 当插入的新节点导致父节点的平衡因子为 -2,并且新节点被插入到左子树的左侧时,发生左左失衡(LL失衡)。
  2. 旋转操作

    • 右单旋的核心目标是将父节点的左子树(即失衡节点的左子树根)提升为新的根节点,并将原来的父节点挂接到新根节点的右子树上。
parent->left = cur->right;  // 将左子树的右子树挂接到父节点的左子树
cur->right = parent;        // 将父节点挂接为左子树的右子树
  1. 处理父节点链接问题

    • 需要确保旋转后父节点的父节点(如果存在)正确地连接到新的根节点。
      • 如果原父节点有父节点(即不是根节点),则要更新父节点的左右子树指向新的根节点。
      • 如果原父节点是根节点,则更新树的根节点。
  2. 更新平衡因子

    • 旋转后,原父节点和新的根节点的平衡因子都应设置为 0,因为旋转使得这两颗子树已经平衡。
  3. 旋转结束

    • 完成旋转后,新的根节点成为子树的根,树恢复平衡。

在这里插入图片描述


2. 右单旋具体会遇到的情况

在这里插入图片描述


3. 右单旋代码总结

// 右单旋
void RotateR(Node* parent)
{Node* cur = parent->_left;Node* curright = cur->_right;parent->_left = curright;if (curright){curright->_parent = parent;}cur->_right = parent;Node* ppnode = parent->_parent;parent->_parent = cur;if (ppnode == nullptr){cur->_parent = nullptr;_root = cur;}else{if (ppnode->_left == parent){ppnode->_left = cur;}else{ppnode->_right = cur;}cur->_parent = ppnode;}// 改变平衡因子parent->_bf = cur->_bf = 0;
}

四、双旋

1. 右左双旋旋转逻辑

形成条件:parent->_bf == 2 && cur->_bf == -1

这里是右左双旋的处理方式:

  1. 插入新节点
  2. 以cur进行右单旋
  3. 以parent进行左单旋

在这里插入图片描述


2. 右左双旋可能会遇到的问题辨析

h = 0 的情况:
在这里插入图片描述

h = 1 的情况:
在这里插入图片描述

h = 2 的情况:
在这里插入图片描述


3. 右左双旋平衡因子的处理

右左双旋的平衡因子与前面的单旋的平衡因子处理方式不同,单旋平衡因子再旋转过后全都是0,但是双旋的平衡因子不一样。

它分为以下三种情况:

  1. h = 0 的情况,及curleft._bf = 0

结果——>parent._bf = 0,cur._bf = 0,curleft._bf = 0

在这里插入图片描述


  1. h > 0 的情况下,及curleft._bf == 1
    以以下C位置插入引发的旋转。

结果: parent._bf = -1,cur._bf = 0,curleft._bf = 0

在这里插入图片描述


  1. h > 0 的情况下,及curleft._bf == -1
    以以下B位置插入引发的旋转。

结果: parent._bf = 0,cur._bf = 1,curleft._bf = 0

在这里插入图片描述


4. 右左双旋代码总结

// 右左双旋
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;cur->_bf = 0;curleft->_bf = 0;}else if (bf == 1){parent->_bf = -1;cur->_bf = 0;curleft->_bf = 0;}else if (bf == -1){parent->_bf = 0;cur->_bf = 1;curleft->_bf = 0;}else{assert(false);}
}

五、左右双旋

形成条件:parent->_bf == -2 && cur->_bf == 1

左右双旋与右左双旋类型,就是反过来的过程~

在这里插入图片描述

在这里插入图片描述

// 左右双旋
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 = 0;cur->_bf = -1;curright->_bf = 0;}else if (bf == -1){parent->_bf = 1;cur->_bf = 0;curright->_bf = 0;}else{assert(false);}
}

总结

那么,到这里就结束啦!

通过学习AVL树的旋转机制,我们不仅掌握了数据结构平衡的重要性,更感受到了算法的巧妙与严谨。

如果对您有帮助的话,麻烦点一个一键三连,谢谢啦~😘😘😘😘

在这里插入图片描述

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

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

相关文章

排序算法(选择排序、直接插入排序、冒泡排序、二路归并排序)(C语言版)

对数组进行排序,主要演示选择排序、直接排序、冒泡排序、二路归并排序算法,附上代码演示 一、编写好各类排序方法的函数 (1) s_sort(int e[],int n):选择排序。 (2)si_sort(int e[],int n):直接插人排序。…

当产业经济插上“数字羽翼”,魔珐有言AIGC“3D视频创作大赛”成功举办

随着AI技术的飞速发展,3D数字人技术已成为驱动各行各业转型升级的重要力量。在这一背景下,2024山东3D数字人视频创作大赛应运而生,并在一番激烈的角逐后圆满落幕,为科技与创意的交融写下浓墨重彩的一笔。 11月20日,一…

Unity-添加世界坐标系辅助线

如果你想在场景中更直观地显示世界坐标系,可以通过编写一个简单的脚本来实现。下面是一个基本的示例脚本,它会在场景中绘制出世界坐标系的三个轴: using UnityEngine;public class WorldAxesIndicator : MonoBehaviour {public float length…

解密自闭症儿童康复秘籍,让孩子重新展开羽翼

在广州这座繁华都市的一隅,隐藏着一片静谧而温暖的天地——星贝育园自闭症儿童寄宿制学校。这里,没有喧嚣与浮躁,只有爱与耐心交织的旋律,为自闭症儿童编织着一个又一个康复的奇迹。星贝育园,如同一盏明灯,…

豆包MarsCode算法题:三数之和问题

问题描述 思路分析 1. 排序数组 目的: 将数组 arr 按升序排序,这样可以方便地使用双指针找到满足条件的三元组,同时避免重复的三元组被重复计算。优势: 数组有序后,处理两个数和 target - arr[i] 的问题可以通过双指针快速找到所有可能的组…

计算机网络socket编程(5)_TCP网络编程实现echo_server

个人主页:C忠实粉丝 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 C忠实粉丝 原创 计算机网络socket编程(5)_TCP网络编程实现echo_server 收录于专栏【计算机网络】 本专栏旨在分享学习计算机网络的一点学习笔记,欢迎大家在评论区交…

游戏引擎学习第21天

虽然没有上一节的难但是内容也很多 关于实现和使用脚本语言 以下是详细复述: 许多人经常问一个问题,反复问过好几次,那就是:是否会在项目中实现脚本语言。这个问题的具体形式通常是:你们会使用脚本语言吗&#xff1…

《线性代数的本质》

之前收藏的一门课,刚好期末复习,顺便看一看哈哈 课程链接:【线性代数的本质】合集-转载于3Blue1Brown官方双语】 向量究竟是什么 线性代数中最基础、最根源的组成部分就是向量,需要先明白什么是向量 不同专业对向量的看法 物理专…

Django+Nginx+uwsgi网站使用Channels+redis+daphne实现简单的多人在线聊天及消息存储功能

网站部署在华为云服务器上,Debian系统,使用DjangoNginxuwsgi搭建。最终效果如下图所示。 一、响应逻辑顺序 1. 聊天页面请求 客户端请求/chat/(输入聊天室房间号界面)和/chat/room_name(某个聊天室页面)链…

鸿蒙多线程开发——线程间数据通信对象03(sendable)

1、简 介 在传统JS引擎上,对象的并发通信开销的优化方式只有一种,就是把实现下沉到Native侧,通过Transferable对象的转移或共享方式降低并发通信开销。而开发者仍然还有大量对象并发通信的诉求,这个问题在业界的JS引擎实现上并没…

OpenHarmony-3.驱动HDF

OpenHarmony HDF 框架 1.OpenHarmony HDF 框架概述 OpenHarmony驱动子系统采用C面向对象编程模型构建,通过平台解耦、内核解耦,兼容不同内核,提供了归一化的驱动平台底座,旨在为开发者提供更精准、更高效的开发环境,力…

ESP8266 STA模式TCP客户端 电脑手机网络调试助手

1.STA模式TCP客户端和电脑网络调试助手 2.STA模式TCP客户端和手机网络调试助手

【JavaEE进阶】 JavaScript

本节⽬标 了解什么是JavaScript, 学习JavaScript的常⻅操作, 以及使⽤JQuery完成简单的⻚⾯元素操作. 一. 初识 JavaScript 1.JavaScript 是什么 JavaScript (简称 JS), 是⼀个脚本语⾔, 解释型或即时编译型的编程语⾔. 虽然它是作为开发Web⻚⾯的脚本语⾔⽽出名,…

神经网络(系统性学习一):入门篇——简介、发展历程、应用领域、基本概念、超参数调优、网络类型分类

相关文章: 神经网络中常用的激活函数 神经网络简介 神经网络(Neural Networks)是受生物神经系统启发而设计的数学模型,用于模拟人类大脑处理信息的方式。它由大量的节点(或称为“神经元”)组成&#xff0…

缓冲区的奥秘:解析数据交错的魔法

目录 一、理解缓存区的好处 (一)直观性的理解 (二)缓存区的好处 二、经典案例分析体会 (一)文件读写流(File I/O Buffering) BufferedOutputStream 和 BufferedWriter 可以加快…

SpringBoot 集成 html2Pdf

一、概述&#xff1a; 1. springboot如何生成pdf&#xff0c;接口可以预览可以下载 2. vue下载通过bold如何下载 3. 一些细节&#xff1a;页脚、页眉、水印、每一页得样式添加 二、直接上代码【主要是一个记录下次开发更快】 模板位置 1. 导入pom包 <dependency><g…

MySQL Join 的原理与优化实践

文章目录 引言一、基础准备&#xff1a;创建环境与示例数据1. 初始化示例表2. 示例 Join 查询3. EXPLAIN 输出分析 二、MySQL Join 的核心算法与执行机制1. 三种 Join 算法的实现与原理1.1 Index Nested-Loop Join&#xff08;INLJ&#xff09;1.2 Simple Nested-Loop Join&…

阿里Qwen系列开源模型介绍

模型种类丰富 Qwen2&#xff1a;包含Qwen2-0.5b、Qwen2-1.5b、Qwen2-7b、Qwen2-57b-a14b以及Qwen2-72b等五种规模的预训练和指令微调模型&#xff0c;其在多语言处理、长文本处理、代码生成、数学和逻辑推理等能力上&#xff0c;在mmlu、gpqa、humaneval等国际测评中得到了验证…

Redis设计与实现 学习笔记 第二十章 Lua脚本

Redis从2.6版本引入对Lua脚本的支持&#xff0c;通过在服务器中嵌入Lua环境&#xff0c;Redis客户端可以使用Lua脚本&#xff0c;直接在服务器端原子地执行多个Redis命令。 其中EVAL命令可以直接对输入的脚本进行求值&#xff1a; 而使用EVALSHA命令则可以根据脚本的SHA1校验…

DevOps 之 CI/CD入门操作 (二)

CI/CD简介 基于Jenkins拉取GitLab的SpringBoot代码进行构建发布到测试环境实现持续集成 基于Jenkins拉取GitLab指定发行版本的SpringBoot代码进行构建发布到生产环境实现CD实现持续部署 一、CI流程 1.1 新建项目 新建一个简单的springboot项目 写一个简单的Controller 运行测…