AVL树简介及其四种旋转

AVL树由二叉搜索树进化而来。在二叉搜索树中如果出现特殊情况:所有插入的数据均为有序,根据二叉搜索树的插入原理,其会退化为单枝斜向下的而二叉树,此时插入,查找,删除的效率也就退化成了O(n),效率太低。为了解决痛点因此引入AVL树。

 AVL树继承了二叉搜索树的大框架,在其上增加了规定:在插入新数据后保证任意树的子树高度不大于1。因此定义了一个整型作为平衡因子,当左子树高度加1,平衡因子减1。右子树高度加1,平衡因子加1。在插入中需要确保任意节点的平衡因子绝对值小于1。

当某个插入某个节点后树中的任意一个节点的平衡因子绝对值大于1,要进行调整,这种调整就称为旋转。

那么什么是旋转如何旋转?

 旋转:
1、让这颗子树左右高度不超过1
2、旋转过程中继续保持他是搜索树
3、更新调整孩子节点的平衡因子
4、让这颗子树的高度跟插入前保持一致

为了更方便对树内节点进行旋转,AVL树在二叉搜索树之上还增加了parent节点:

template<class T>
struct AVLTree
{AVLTreeNode(const T& data = T()): left(nullptr), right(nullptr), pare(nullptr), k(data), bf(0){}AVLTreeNode<T>* left;AVLTreeNode<T>* right;AVLTreeNode<T>* pare;T k;int bf; //平衡因子
};

二叉搜索树的插入:

bool Insert(const K& key)
    {
        Node* cur = _root;
        Node* pare = nullptr;
        while (cur)
        {
            if (key < cur->k)
            {
                pare = cur;
                cur = cur->right;
            }
            else if (key > cur->k)
            {
                pare = cur;
                cur = cur->left;
            }
            else if (key == cur->k) return false;
        }
            Node* newnode = Node(key);
            if (key < pare->k) 
             {newnode = pare->right;
               newnode->pare = pare;

                pare->bf++;//更新平衡因子
              }
            else if (key > pare->k)
            {newnode = pare->left;
             newnode->pare = pare;

             pare ->bf--;
             }
            //到此为止是二叉树的插入

         
    }

                    //  接下来开始判定是否有平衡因子绝对值大于2,从当前插入的节点的父节点开始,依次向上遍历,一共有这几种情况:
            while(pare) //遍历到根节点
           {
             if(pare->bf == 0)//此时插入节点的父节点平衡因子为0,代表插入前父节点的左子树高度比右子树大1,或者右子树高度比左子树大1,插入后填补了高度差,但对于整个树来说,其高度没有改变,因此可以断定整个树的高度没有改变,直接退出循环。

          break;

          else if(pare->bf == -1 || pare->bf == 1) //此时如果新插入节点的父节点的平衡因子不为0,那么就代表插入节点前父节点为0,其子树是平衡的,插入后树的高度发生了变化。由于高度的变化可能对上层的节点平衡因子产生影响,此时就要找到父节点的父节点去查看它的情况。

  {        cur = pare;   pare = pare->pare;}

     else if(pare ->bf == -2 || pare->bf == 2)//此时违反了AVL树的规定,需要进行旋转,旋转后pare的平衡因子变为0,可以直接break,不需要再继续往上遍历。

 //  AVL树的旋转一共有四种情况:

 //通过抽象图对四种情况进行解析,图中的h代表任意高度为h的树(h为任意不小于0的整数),由于AVL树的调整不关心树的形状,只关心树的高度,因此抽象成了一个矩形。

       (1)左单旋:

当产生如图情况,20的右子树高度加一导致10的左右高度差绝对值为2,我们需要将20这个节点往上提,10这个节点网上压,导致10旋转到20的左边,称为左单旋。左单旋的具体实现是,将pare(pare是10)变为cur(cur是20)的左树,然后cur的原左树变为pare的右树,也符合二叉搜索树的性质。

void RotateL(Node * pare){
Node* cur = pare -> right;
Node* subR = cur ->right;
ppare = pare ->pare;
cur -> left = pare;
pare->right = subR;
pare -> pare = cur;

if(subR != nullptr)
subR ->pare = pare;
if(ppare == nullptr)//此时pare为根
  {_root == cur;
   _root ->pare = nullptr;
}
else //如果pare不为根,分情况讨论pare是ppare的左树还是右树
{
  if(ppare ->key < cur)
  ppare-> right = cur;
else
ppare ->left = cur;
   }
//接下来更新平衡因子
pare ->bf = cur->bf =0;
}

(2)右单旋:

产生如图情况,20这个节点的右子树高度加一导致其平衡因子变为2,需要将20往下压,10向上提,20旋转到10的右边,称为右单旋。右单旋的具体实现是将pare(也就是20)变为cur的右树,cur的原右树变为pare的左树。

void RotateR(Node* pare){
Node* cur = pare ->left;
Node* subR = cur ->left;
Node* ppare = pare ->pare;
cur ->right = pare;
pare ->left = subR;
if(subR != nullptr)
subR ->pare = pare;
if(ppare == nullptr)
  {
     _root = cur;
    cur ->pare = nullptr;
  }
else
  {
     if(ppare ->k < cur->k)
        ppare ->right = cur;
    else
       ppare ->left = cur;
    }
cur->bf = pare->bf = 0;
}

(3)左右双旋

在这种情况下要进行两次旋转,先对10这个节点进行左单旋,再对30这个节点进行右单旋。并且20的平衡因子变为0,10的平衡因子变为0,30的平衡因子变为-1。

void RotateLR(Node* pare)
{
 Node * cur = pare->left;
 Node * subR = cur ->right;
 RotateL(cur);
 RotateR(pare);
 if(subR->bf == -1) //两种情况,在subR(图中的20节点)的左或者右插入
   {
     cur->bf = subR->bf = 0;
     pare->bf = 1;
    }
 else if(subR ->bf == 1)//插入在subR的右边
    {
       pare ->bf = subR ->bf = 0;
       cur->bf = -1;
    }

else if(subR ->bf == 0)//subR自己就是新增节点

  {

pare ->bf = cur ->bf = subR->bf = 0;

  }
}

(4)右左双旋

也就是上一种情况的反向情况,如图:

先对30进行右单旋,再对10进行左单旋即可。

void RotateLR(Node* pare)
{
 Node * cur = pare->right;
 Node * subR = cur ->left;
 RotateR(cur);
 RotateL(pare);
 if(subR->bf == -1) 
   {
     pare->bf = subR->bf = 0;
     cur->bf = 1;
    }
 else if(subR ->bf == 1)
    {
       cur ->bf = subR ->bf = 0;
       pare ->bf = -1;
    }

else if(subR ->bf == 0)

  {

pare ->bf = cur ->bf = subR->bf = 0;

  }
}

插入代码如下:

bool Insert(const k& data){if (_root == nullptr){_root = new Node(kv);return true;}Node* parent = nullptr;Node* cur = _root;while (cur){if (cur->k < k){pare = cur;cur = cur->_right;}else if (cur->k > k){pare = cur;cur = cur->_left;}else{return false;}}cur = new Node(k);if (pare->k< kt){pare->right = cur;cur->pare = pare;}else{pare->left = cur;cur->pare = pare;}while (pare) {if (cur == pare->left){pare->bf--;}else{pare->bf++;}if (pare->bf == 0){break;}else if (pare->_bf == 1 || pare->bf == -1){cur = pare;pare = pare->pare;}else if (pare->bf == 2 || pare->bf == -2){if (pare->bf == 2 && cur->bf == 1){RotateL(pare);}else if (pare->bf == -2 && cur->bf == -1){RotateR(pare);}else if (pare->bf == -2 && cur->bf == 1){RotateLR(pare);}else if (pare->bf == 2 && cur->bf == -1){RotateRL(pare);}else{assert(false);}break;}else{assert(false);}}return true;}

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

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

相关文章

CUDA编程 - 用向量化访存优化 elementwise 核函数 - 学习记录

Cuda elementwise 一、简介1.1、ElementWise1.2、 float4 - 向量化访存 二、实践2.1、如何使用向量化访存2.2、Cuda elementwise - Add2.3、Cuda elementwise - Sigmoid2.3.1、简单的 Sigmoid 函数2.3.2、ElementWise Sigmoid float4&#xff08;向量化访存&#xff09; 2.4、C…

js里面有引用传递吗?

一&#xff1a;什么是引用传递 引用传递是相对于值传递的。那什么是值传递呢&#xff1f;值传递就是在传递过程中再复制一份&#xff0c;然后再赋值给变量&#xff0c;例如&#xff1a; let a 2; let b a;在这个代码中&#xff0c;let b a; 就是一个值传递&#xff0c;首先…

从零开始学Spring Boot系列-Hello World

欢迎来到从零开始学Spring Boot的旅程&#xff01;我们将从一个非常基础但重要的示例开始&#xff1a;创建一个简单的Spring Boot应用程序&#xff0c;并输出“Hello World”。 1. 环境准备 首先&#xff0c;确保你的开发环境已经安装了以下工具&#xff1a; Java Development …

读人工不智能:计算机如何误解世界笔记04_数据新闻学

1. 计算化和数据化的变革 1.1. 每一个领域都在进行计算化和数据化的变革 1.1.1. 出现了计算社会科学、计算生物学、计算化学或其他数字人文学科 1.1.2. 生活已走向计算化&#xff0c;人们却一点也没有变 1.2. 在如今的计算化和数据化世界中&#xff0c;调查性新闻的实践必须…

掌握ChatGPT润色绝技:什么是人工智能写作以及如何使用它来完成写作任务

如对AI写论文感兴趣&#xff0c;欢迎添加作者wx讨论 : ryan_2982 人工智能 (AI) 的出现开创了技术进步的新时代&#xff0c;彻底改变了包括写作和内容创作在内的各个行业。人工智能写作和人工智能提示已成为可以简化和增强写作任务的强大工具。在这篇博文中&#xff0c;我们将…

2018-02-14 新闻内容爬虫【上学时做论文自己爬新闻数据,原谅我自己懒发的图片】

2018-02-14新闻内容爬虫【上学时做论文自己爬新闻数据&#xff0c;原谅我自己懒发的图片】资源-CSDN文库https://download.csdn.net/download/liuzhuchen/88878591爬虫过的站点&#xff1a; 1QQ新闻 1&#xff0c;准备爬取滚动新闻页面 2 通过F12 开发工具查找发现&#xff…

k8s节点负载使用情况分析命令kubectl describe node [node-name]

1.到任意安装了kubectl节点命令的节点上执行kubectl describe node [node-name] 上面的Requests最小分配 Limits最大分配是所有pod之和&#xff0c;最小分配之和不能超过服务器实际参数&#xff0c;否则新的pod会因为资源不够起不来&#xff0c;最大分配是预设之和&#xff0…

office word保存pdf高质量设置

1 采用第三方pdf功能生成 分辨率越大质量越好

leetcode(算法) 83.删除排序链表中的重复元素(python版)

需求 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,1,2] 输出&#xff1a;[1,2] 示例 2&#xff1a; 输入&#xff1a;head [1,1,2,3,3] 输出&…

Android WebView访问网页+自动播放视频+自动全屏+切换横屏

一、引言 近期&#xff0c;我发现电视家、火星直播等在线看电视直播的软件都已倒闭&#xff0c;而我奶奶也再无法通过这些平台看电视了。她已六十多岁&#xff0c;快七十岁啦。这些平台的倒下对我来说其实没有多大的影响&#xff0c;但是对于文化不多的她而言&#xff0c;生活中…

大模型学习笔记四:LangChain开发框架解析

文章目录 一、langChain核心组件介绍二、模块I/O封装1&#xff09;多轮对话 Session 封装2&#xff09;模型的输入&#xff08;1&#xff09;Prompt模板封装&#xff08;2&#xff09;从文件加载Prompt模板 3&#xff09;模型的输出&#xff08;1&#xff09;Pydantic (JSON) P…

c入门第二十四篇: 学生成绩管理系统优化(可执行文件传参)

前言 我&#xff1a;“师弟&#xff0c;review完你的代码之后&#xff0c;你觉得有没有什么地方可以优化&#xff1f;” 师弟一脸懵。 我&#xff1a;“比如&#xff0c;你把客户端和服务端的可执行文件生成之后&#xff0c;我把服务端部署到我的测试机器上&#xff0c;客户端…

通过css修改video标签的原生样式

通过css修改video标签的原生样式 描述实现结果 描述 修改video标签的原生样式 实现 在控制台中打开设置&#xff0c;勾选显示用户代理 shadow DOM&#xff0c;就可以审查video标签的内部样式了 箭头处标出来的就是shodow DOM的内容&#xff0c;这些内容正常不可见的&#x…

下载huggingface数据集到本地并读取.arrow文件遇到的问题

文章目录 1. 524MB中文维基百科语料&#xff08;需要下载的数据集&#xff09;2. 下载 hugging face 网站上的数据集3. 读取 .arrow 文件报错代码4. 纠正后代码 1. 524MB中文维基百科语料&#xff08;需要下载的数据集&#xff09; 2. 下载 hugging face 网站上的数据集 要将H…

07_第七章 前端工程化(es6,Vue3,Element_plus组件库)

文章目录 第七章 前端工程化一、前端工程化开篇1.1 什么是前端工程化1.2 前端工程化实现技术栈 二、ECMA6Script2.1. es6的介绍2.2 es6的变量和模板字符串2.3 es6的解构表达式2.4 es6的箭头函数2.4.1 声明和特点2.4.2 实践和应用场景2.4.3 rest和spread 2.5 es6的对象创建和拷贝…

绝地求生:春节部分活动将结束,3月有新版本上线,通行证不偷懒可换成长型

嗨&#xff0c;我是闲游盒~ 感觉过年就在眼前但是已经结束了&#xff0c;时间过的太快了又回归了工作的生活中&#xff0c;而年前更新的28.1新春版本也进行到了小一半的进度。 ◆ 春节版本部分活动即将结束 在大厅首页右上角的活动中心里&#xff0c;春节积分商店和觉醒之旅活动…

MATLAB环境下一种改进的瞬时频率(IF)估计方法

相对于频率成分单一、周期性强的平稳信号来说&#xff0c;具有非平稳、非周期、非可积特性的非平稳信号更普遍地存在于自然界中。调频信号作为非平稳信号的一种&#xff0c;由于其频率时变、距离分辨率高、截获率低等特性&#xff0c;被广泛应用于雷达、地震勘测等领域。调频信…

sqllabs第46关 order by 注入(通过盲注)

打开第46关 提示我们(请将参数输入为sort&#xff08;带数值&#xff09;) 用sort注入排序 尝试操作 order by注入 什么是order by 在MySQL支持使用ORDER BY语句对查询结果集进行排序处理&#xff0c;使用ORDER BY语句不仅支持对单列数据的排序&#xff0c;还支持对数据表中…

vulnhub----hackme2-DHCP靶机

文章目录 一&#xff0c;信息收集1.网段探测2.端口扫描3.目录扫描 二&#xff0c;信息分析三&#xff0c;sql注入1.判断SQL注入2.查询显示位3.查询注入点4.查询库5.查询表6.查字段7. 查user表中的值8.登陆superadmin用户 四&#xff0c;漏洞利用文件上传命令执行蚁剑连接 五&am…

商家入驻平台怎么让资金自动分配给商家

最近很多上线了多商户电商系统的朋友咨询&#xff0c;我们平台的用户支付后&#xff0c;钱进入了我们的对公账户&#xff0c;怎样让钱在走完流程后&#xff0c;自动进入商家的账户呢&#xff1f;今天商淘云为您分享商户入驻平台自动分配给商家资金的三种方法。 首先是平台应建立…