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.rhkb.cn/news/477949.html

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

相关文章

文小言1:

✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…

uni-app 界面TabBar中间大图标设置的两种方法

一、前言 最近写基于uni-app 写app项目的时候,底部导航栏 中间有一个固定的大图标,并且没有激活状态。这里记录下实现方案。效果如下(党组织这个图标): 方法一:midButton的使用 官方文档:ta…

CentOS7(Linux)详细安装教程(图文详解)

一、软件准备 本文CentOS7安装在VMware Workstation虚拟机软件,故安装前请自行安装该软件。VMware Workstation官网链接:VMware Workstation官网地址CentOS7下载地址:centos7镜像 如下是最常使用的版本(任选版本)centos-7.9.2009-isos-x86_64安装包下载_开源镜像站-阿里…

【实战】基于urllib和BeautifulSoup爬取jsp网站的数据

文章目录 前言目标网站分析目标网页爬取数据解析导出数据其他问题处理分页检索及多关键字搜索去重cookie问题工具封装经验总结前言 网络数据爬取大致分为两类: 静态爬取:该种方式针对那种架构比较老的网站,使用模版方式,通过浏览器F12只能找到静态页面,找不到返回json数…

玩转数字与运算:用C语言实现24点游戏的扑克牌魅力

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…

【MySQL】sql注入相关内容

【MySQL】sql注入相关内容 1. 为什么使用sql注入的时候,url传值的时候要使用–而不是– 使用–进行注释的时候需要在后面加一个空格才可以被认为是注释,url传值的过程中会将空格自动忽略,使用则可以在传输中保留为空格符号。(同…

shell脚本(完结)

声明:学习视频来自b站up主 泷羽sec,如涉及侵权马上删除文章 感谢泷羽sec 团队的教学 视频地址:shell编程(完结)_哔哩哔哩_bilibili 本文主要讲解不同shell脚本中的相互调用以及输入输出重定向操作。 一、不同脚本之间…

【bug】使用transformers训练二分类任务时,训练损失异常大

使用transformers训练二分类任务时,训练损失异常大 问题分析 问题 training_loss异常大,在二分类损失中,收敛在1~2附近,而eval_loss却正常(小于0.5) 分析 参考: Bug in gradient accumulation…

深入解析 EasyExcel 组件原理与应用

✨深入解析 EasyExcel 组件原理与应用✨ 官方:EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网 在日常的 Java 开发工作中,处理 Excel 文件的导入导出是极为常见的需求。 今天,咱们就一起来深入了解一款非常实用的操作 Exce…

Gradio学习笔记记录

安装指令:pip install gradio方法介绍 Interface》用于构建一些简单的页面,可以直接用这个指令搞定 形式》接收三个参数分别为处理函数、输入、输出三部分,呈现一般左/上为输入,右或下为输出 fn:将用户界面 &#xff0…

养老院管理系统+小程序项目需求分析文档

智慧综合养老服务平台是以业务为牵引、场景为驱动,围绕“老人”业务域,持续沉淀和打磨形成适应不同养老业务发展需要的业务能力,推动业务模式升级,为养老服务提供数字化解决方案,并依托实体站点与养老机构实现线上线下…

React的基本知识:事件监听器、Props和State的区分、改变state的方法、使用回调函数改变state、使用三元运算符改变state

这篇教学文章涵盖了大量的React基本知识。 包括: 事件监听器Props和State的区分改变state的方法使用回调函数改变state使用三元运算符改变state处理state中的数组处理state中的object条件渲染 &&条件渲染 三元运算符React中的forms 1. Event Listeners 在…

repmgr安装及常用运维指令

简介 repmgr 由 EDB 与其他个人和组织的贡献一起开发,安装部署相对较为简单 安装 repmgr官网上传对应的安装到服务器上 安装前/etc/hosts IP映射、始终同步、免密通信本文忽略 repmgr的安装相对较为简单,目前repmgr-5仅仅支持到postgresql-15 postgresql必要参数…

opencv-python 分离边缘粘连的物体(距离变换)

import cv2 import numpy as np# 读取图像,这里添加了判断图像是否读取成功的逻辑 img cv2.imread("./640.png") # 灰度图 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 高斯模糊 gray cv2.GaussianBlur(gray, (5, 5), 0) # 二值化 ret, binary cv2…

SATA接口不通分析案例分享

问题: 反馈有台NVR的某个接口SATA不通(共有4个SATA接口,采用SATA HUB JMB575),挂载硬盘不上。 分析: 1、直接对换问题口SATA1与正常口SATA2的SATA数据线,SATA1口还是异常,挂在不上…

【Web前端】如何构建简单HTML表单?

HTML 表单是 Web 开发中非常重要的组成部分。它们是与用户交互的主要方式,能够收集用户输入的数据。表单的灵活性使它们成为 HTML 中最复杂的结构之一,但若使用正确的结构和元素,可以确保其可用性和无障碍性。 表单的基本结构 HTML 表单使用…

Flutter:AnimatedIcon图标动画,自定义Icon通过延时Interval,实现交错式动画

配置vsync&#xff0c;需要实现一下with SingleTickerProviderStateMixinclass _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin{// late延迟初始化 AnimationControllerlate AnimationController _controller;overridevoid initStat…

PyQt学习笔记

一.PyQt5的安装 当我们安装好开发环境后&#xff0c;打开pycharm在其设置里面点击按钮自动安装即可。 安装完成后我们会在这里面看到这几个东西说明安装成功了。 二.PyQt5 GUI程序框架 1.一个简单的PyQt5应用程序 首先我们用pycharm创建一个demo.py的文件。 我们创建文件为s…

HTML5好看的音乐播放器多种风格(附源码)

文章目录 1.设计来源1.1 音乐播放器风格1效果1.2 音乐播放器风格2效果1.3 音乐播放器风格3效果1.4 音乐播放器风格4效果1.5 音乐播放器风格5效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&…

ReactPress(阮一峰推荐工具):一款基于Next.js的免费开源博客CMS系统

ReactPress Github项目地址&#xff1a;https://github.com/fecommunity/reactpress 欢迎Star。 此项目是用于构建博客网站的&#xff0c;包含前台展示、管理后台和后端。 此项目是基于 React antd NestJS NextJS MySQL 的&#xff0c;项目已经开源&#xff0c;项目地址在 …