当前位置: 首页 > news >正文

AVL树实现

1.AVL的概念

    1.AVL树属于二叉搜索树的一种,但它不同与普通的二叉搜索树还具有以下的性质:

    每一个根的左右子树的高度差的绝对值不超过1。AVL树是通过高度差去控制平衡的,所以又称作为平衡二叉搜索树。

    2.AVL树实现我们引入了一个平衡因子的概念,每一个结点都有它的平衡因子,所谓平衡因子即结点的右子树的高度减去左子树的高度,AVL树的平衡因子有0/1/-1三种,如果平衡因子不属于这三种之一便不是AVL树。

    3.AVL树整体结点数量和分布和完全二叉树类似,高度可以控制在logN,那么增删查改的效率也可以控制在O(logN)

   下图就为典型的AVL树:

2.AVL树的实现 

   2.1AVL树的结构

 

template<class K, class V>

struct AVLTreeNode

{

// 需要parent指针,后续更新平衡因子可以看到

     pair<K, V> _kv;

    AVLTreeNode<K, V>* _left;

    AVLTreeNode<K, V>* _right;

    AVLTreeNode<K, V>* _parent;

    int _bf;                                                 // 平衡因子

AVLTreeNode(const pair<K, V>& kv)

    :_kv(kv)

    , _left(nullptr)

    , _right(nullptr)

    , _parent(nullptr)

    , _bf(0)

     {}

};

template<class K, class V>

class AVLTree

 {

       typedef AVLTreeNode<K, V> Node;

public:

    bool Insert(const pair<K, V>& kv)

 {

    if (_root == nullptr)

{

    _root = new Node(kv);

   return true;

 }

Node* parent = nullptr;

Node* cur = _root;

while (cur)

 {

if (cur->_kv.first < kv.first)

 {

    parent = cur;

    cur = cur->_right;

 }

else if (cur->_kv.first > kv.first)

 {

    parent = cur;

    cur = cur->_left;

 }

else

  {

    return false;

  }

}

  cur = new Node(kv);

if (parent->_kv.first < kv.first)

{

  parent->_right = cur;

}

else

{

  parent->_left = cur;

}

// 链接父亲

cur->_parent = parent;

// 控制平衡

// 更新平衡因子

while (parent)

{

if (cur == parent->_left)

    parent->_bf--;

else

     parent->_bf++;

if (parent->_bf == 0)

{

    break;

}

else if (parent->_bf == 1 || parent->_bf == -1)

{

    cur = parent;

    parent = parent->_parent;

}

else if (parent->_bf == 2 || parent->_bf == -2)

{

   // 旋转

    break;

}

else

{

    assert(false);

 }

}

   return true;

}

private:

   Node* _root = nullptr;

};

上面为一个完整的AVL的结构,接下来就是对AVL树的深层次了解

   2.2  AVL树的插入 

      2.2.1 AVL树插入一个值的过程

1.插入一个值按二叉搜索树的规则进行插入

2.如果插入后平衡因子都符合AVL树的规则插入就结束

3.如果不符合AVL树的规则,就更新平衡因子,对不平衡子树进行旋转

     2.2.2 平衡因子更新

更新的原则:

1. 平衡因子 = 0/1/-1;

2.插入的结点在右子树则parent的平衡因子++,在左子树则parent的平衡因子--

更新停止条件:

1.更新后parent的平衡因子等于0,更新前的平衡因子为1或者-1,说明一边高一边低,而插入的新结点插入在低的一边,插入后parent所在的子树高度不变,不会影响parent的父亲结点的平衡因子,更新结束。

2.更新后parent的平衡因子等于1或者-1,说明在更新前parent的平衡因子等于0,parent的左右子树一样高,但插入新结点后高度增加1了,就会影响parent的父结点,如果此时父结点不符合AVL树的规则就要去继续更新。

3.更新后parent的平衡因子等于2或者-2,说明更新前parent的平衡因子等于1或者-1,而新插入的结点恰好在较高的子树上。破坏了平衡,parent所在的子树不符合平衡要求,则需要旋转处理。

旋转的目标有两个:        1.把parent子树旋转平衡

                                        2.降低parent子树的高度,恢复到插入结点以前的高度,所以旋转后插入                                             结束。

 2.3 旋转

     2.3.1 旋转的原则

1.保持搜索树的原则

2.让旋转的树从不满足变平衡,其次降低旋转树的高度

旋转总共分为四种:左单旋/右单旋/左右双旋/右左双旋

      2.3.2 右单旋

在上图中就是右单旋的流程图, 有a/b/c抽象为三颗高度为h的子树(h>=0),a/b/c均符合AVL树的要求,10可能是整数的根也可能是一个整颗树局部的子树的根,该图也只是右单旋的形态之一,但易于理解。

如上图所示:在a点插入了一个新结点导致10的平衡因子变为了-2,为让其符合平衡规则,需要让10的过高的的左子树右旋,如第三步所示,从而控制两颗树的平衡。

核心步骤:因为5 < b子树的值 < 10,将b子树作为10结点的左子树,然后将5变成这颗新树的根,符合了搜索树的规则,控制了平衡,同时这颗树恢复到了之前的h+2,符合旋转原则。

以下的情况就不细讲了,根据第一张的思维可以理解下面的情况:

  2.3.3 右单旋代码的实现 

void RotateR(Node* parent)

     Node* subL = parent->_left;

     Node* subLR = subL->right;

     Node*Pparent = parent ->_parent;

    

     parent ->left = subLR;

     if(subLR)

     subLR->_parent = parent;

     parent ->_parent = subL;

     if(Pparent ==nullptr)

    {

        _root = subL;

        subL->_parent =nullptr;

    }

    else

     {

          if (parent ==Pparent->_left)
         {
             Pparent->_left = subL;
         }
        else
        {
           Pparent->_right = subL;
        }
          subL->_parent = Pparent;
     }

      subL->_parent = subL->_bf = 0;

}

2.3.4 左单旋 

  左单旋与右单旋类似,只是从原本过高的左子树的右旋变成现在的过高的右子树的左旋。

其他的情况也和右子树的情况类似这里就不一一列举了

 2.3.5 左单旋代码的实现

void RotateRL(Node* parent)

{
      Node*subR = parent->right;

      Node*subRL = parent->left;

      Node*Pparent = parent->_parent;

    

     parent ->_right =  subRL;

     if(subRL)

     subRL->_parent = parent;

     subR->_left =  parent;

     parent->_parent = subR;

     

    if(Pparent == nullptr)

  {

      _root = subR;

     subR->_parent = nullptr;

  }

  else if(Pparent->_left = parent)

 {

       Pparent ->_left = subR;

  }

  else 

  {

      Pparent ->_right = subR;

  } 

  subR->_parent = Pparent;

  parent->_bf = subR->_bf = 0;

}

 2.3.6 左右双旋

    

从上图可以看出右单旋没有解决两颗树的平衡问题,所以在这里就要使用左右双旋才能解决该问题。

 

在上图中有三个场景:

   场景1:h>=1时,新增结点插入在e子树,e子树高度从h-1并为h并不断更新8->5->10平衡因子,引发旋转,其中8的平衡因子为1,旋转后8和5平衡因子为0,10的平衡因子变为1.

   场景2:h >= 1时,新增结点插⼊在f⼦树,f⼦树⾼度从h-1变为h并不断更新8->5->10平衡因⼦,引发旋转,其中8的平衡因⼦为1,旋转后8和10平衡因⼦为0,5平衡因⼦为-1。

   场景3:h == 0时,a/b/c都是空树,b⾃⼰就是⼀个新增结点,不断更新5->10平衡因⼦,引发旋 转,其中8的平衡因⼦为0,旋转后8和10和5平衡因⼦均为0。

 2.3.7 左右双旋代码实现

void RotateLR(Node* parent)

{
   Node*subL = parent ->_left;

   Node*subLR = subL->_right;

   int bf = subLR->_bf;

 

  Rotatel(parent->_left);

  RotateR(parent);

   

     if (bf == 0)
  {
     subL->_bf = 0;
     subLR->_bf = 0;
     parent->_bf = 0;
  }
  else if (bf == -1)
  {
   subL->_bf = 0;
   subLR->_bf = 0;
   parent->_bf = 1;
  }
    else if(bf == 1)
  {
   subL->_bf = -1;
   subLR->_bf = 0;
   parent->_bf = 0;
  } 
   else
  {
    assert(false);
  }
}

2.3.8 右左双旋 

  同样分三个场景:

   场景1:h >= 1时,新增结点插⼊在e⼦树,e⼦树⾼度从h-1变为h并不断更新12->15->10平衡因 ⼦,引发旋转,其中12的平衡因⼦为-1,旋转后10和12平衡因⼦为0,15平衡因⼦为1。

  场景2:h >= 1时,新增结点插⼊在f⼦树,f⼦树⾼度从h-1变为h并不断更新12->15->10平衡因⼦,引发旋转,其中12的平衡因⼦为1,旋转后15和12平衡因⼦为0,10平衡因⼦为-1。

 场景3:h == 0时,a/b/c都是空树,b⾃⼰就是⼀个新增结点,不断更新15->10平衡因⼦,引发旋 转,其中12的平衡因⼦为0,旋转后10和12和15平衡因⼦均为0。

2.3.9 右左双旋代码实现 

void RotateRL(Node* parent)
{
  Node* subR = parent->_right;
  Node* subRL = subR->_left;
  int bf = subRL->_bf;
  RotateR(parent->_right);
  RotateL(parent);
  if (bf == 0)
{
   subR->_bf = 0;
   subRL->_bf = 0;
   parent->_bf = 0;
}
else if (bf == 1)
{
   subR->_bf = 0;
   subRL->_bf = 0;
   parent->_bf = -1;
}
else if (bf == -1)
{
  subR->_bf = 1;
  subRL->_bf = 0;
  parent->_bf = 0;
}
else
{
  assert(false);
}
}

相关文章:

AVL树实现

1.AVL的概念 1.AVL树属于二叉搜索树的一种&#xff0c;但它不同与普通的二叉搜索树还具有以下的性质&#xff1a; 每一个根的左右子树的高度差的绝对值不超过1。AVL树是通过高度差去控制平衡的&#xff0c;所以又称作为平衡二叉搜索树。 2.AVL树实现我们引入了一个平衡因子的概…...

初始MYSQL数据库(6)—— 事务

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a; MYSQL 目录 事务的概念 事务的ACID特性 使用事务 查看支持事务的存储引擎 事务的语法 保存点 自动/手动提交事务 事务的隔离性和…...

0基础学习PyTorch——GPU上训练和推理

大纲 创建设备训练推理总结 在《Windows Subsystem for Linux——支持cuda能力》一文中&#xff0c;我们让开发环境支持cuda能力。现在我们要基于《0基础学习PyTorch——时尚分类&#xff08;Fashion MNIST&#xff09;训练和推理》&#xff0c;将代码修改成支持cuda的训练和推…...

这款免费工具让你的电脑焕然一新,专业人士都在用

HiBit Uninstaller 采用单一可执行文件的形式,无需复杂的安装过程,用户可以即刻开始使用。这种便捷性使其成为临时使用或紧急情况下的理想选择。尽管体积小巧,但其功能却异常强大,几乎不会对系统性能造成任何负面影响。 这款工具的一大亮点是其多样化的功能。它不仅能够常规卸…...

Java高级Day52-BasicDAO

138.BasicDao 基本说明&#xff1a; DAO&#xff1a;data access object 数据访问对象 这样的通用类&#xff0c;称为 BasicDao&#xff0c;是专门和数据库交互的&#xff0c;即完成对数据库(表)的crud操作 在BasicDao 基础上&#xff0c;实现一张表对应一个Dao&#xff0c;…...

【OceanBase 诊断调优】—— SQL 诊断宝典

视频 OceanBase 数据库 SQL 诊断和优化&#xff1a;https://www.oceanbase.com/video/5900015OB Cloud 云数据库 SQL 诊断与调优的应用实践&#xff1a;https://www.oceanbase.com/video/9000971SQL 优化&#xff1a;https://www.oceanbase.com/video/9000889阅读和管理SQL执行…...

微服务Redis解析部署使用全流程

目录 1、什么是Redis 2、Redis的作用 3、Redis常用的五种基本类型&#xff08;重要知识点&#xff09; 4、安装redis 4.1、查询镜像文件【省略】 4.2、拉取镜像文件 4.3、启动redis并设置密码 4.3.1、修改redis密码【可以不修改】 4.3.2、删除密码【坚决不推荐】 5、S…...

C++之STL—常用排序算法

sort (iterator beg, iterator end, _Pred) // 按值查找元素&#xff0c;找到返回指定位置迭代器&#xff0c;找不到返回结束迭代器位置 // beg 开始迭代器 // end 结束迭代器 // _Pred 谓词 random_shuffle(iterator beg, iterator end); // 指定范围内的元素随机调…...

【驱动】地平线X3派:备份与恢复SD卡镜像

1、备份镜像 1.1 安装gparted GParted是硬盘分区软件GNU Parted的GTK+图形界面前端,是GNOME桌面环境的默认分区软件。 GParted可以用于创建、删除、移动分区,调整分区大小,检查、复制分区等操作。可以用于调整分区以安装新操作系统、备份特定分区到另一块硬盘等。 在Ubun…...

【C++报错已解决】std::ios_base::failure

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…...

matlab入门学习(四)多项式、符号函数、数据统计

一、多项式 %多项式&#xff08;polynomial&#xff09;%创建 p[1,2,3,4] %系数向量&#xff0c;按x降幂排列&#xff0c;最右边是常数&#xff08;x的0次幂&#xff09; f1poly2str(p,x) %系数向量->好看的字符串 f x^3 2 x^2 3 x 4&#xff08;不能运算的式子&#xf…...

leetcode621. 任务调度器

给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表&#xff0c;用字母 A 到 Z 表示&#xff0c;以及一个冷却时间 n。每个周期或时间间隔允许完成一项任务。任务可以按任何顺序完成&#xff0c;但有一个限制&#xff1a;两个 相同种类 的任务之间必须有长度为 n 的冷却时…...

Spark 的 Skew Join 详解

Skew Join 是 Spark 中为了解决数据倾斜问题而设计的一种优化机制。数据倾斜是指在分布式计算中&#xff0c;由于某些 key 具有大量数据&#xff0c;而其他 key 数据较少&#xff0c;导致某些分区的数据量特别大&#xff0c;造成计算负载不均衡。数据倾斜会导致个别节点出现性能…...

讯飞星火编排创建智能体学习(一)最简单的智能体构建

目录 开篇 智能体的概念 编排创建智能体 创建第一个智能体 ​编辑 大模型节点 测试与调试 开篇 前段时间在华为全联接大会上看到讯飞星火企业级智能体平台的演示&#xff0c;对于拖放的可视化设计非常喜欢&#xff0c;刚开始以为是企业用户才有的&#xff0c;回来之后查…...

mac-m1安装nvm,docker,miniconda

1.安装minicondaMAC OS(M1)安装配置miniconda_mac-mini m1 conda-CSDN博客 2.安装nvm&#xff08;用第二个方法&#xff09;Mac电脑安装nvm(node包版本管理工具)-CSDN博客 3.安装docker dmg下载链接docker-toolbox-mac-docker-for-mac安装包下载_开源镜像站-阿里云 教程MacOS系…...

STM32F407之Flash

寄存器分类 一般寄存器分为只读存储器 (ROM) 随机存储器(RAM) 只读存储器 只读存储器也被称为ROM 在正常工作时只能读不能写。 只读存储器经历的阶段 ROM->PROM->EPROM->EEPROM ->Flash 优点&#xff1a;掉电不丢失&#xff0c;解构简单 缺点&#xff1a;只适…...

优化 Go 语言数据打包:性能基准测试与分析

场景&#xff1a;在局域网内&#xff0c;需要将多个机器网卡上抓到的数据包同步到一个机器上。 原有方案&#xff1a;tcpdump -w 写入文件&#xff0c;然后定时调用 rsync 进行同步。 改造方案&#xff1a;使用 Go 重写这个抓包逻辑及同步逻辑&#xff0c;直接将抓到的包通过网…...

【SQL】未订购的客户

目录 语法 需求 示例 分析 代码 语法 SELECT columns FROM table1 LEFT JOIN table2 ON table1.common_field table2.common_field; LEFT JOIN&#xff08;或称为左外连接&#xff09;是SQL中的一种连接类型&#xff0c;它用于从两个或多个表中基于连接条件返回左表…...

Qt(9.28)

widget.cpp #include "widget.h"Widget::Widget(QWidget *parent): QWidget(parent) {QPushButton *btn1 new QPushButton("登录",this);this->setFixedSize(640,480);btn1->resize(80,40);btn1->move(200,300);btn1->setIcon(QIcon("C:…...

javascript-冒泡排序

前言&#xff1a;好久没学习算法了&#xff0c;今天看了一个视频课&#xff0c;之前掌握很好的冒泡排序居然没写出来&#xff1f; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport"…...

Flask RESTful 示例

目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题&#xff1a; 下面创建一个简单的Flask RESTful API示例。首先&#xff0c;我们需要创建环境&#xff0c;安装必要的依赖&#xff0c;然后…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...