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

【C++】AVL树(平衡二叉树)

目录

  • 一、AVL树的定义
  • 二、AVL树的作用
  • 三、AVL树的插入操作
    • 插入——平衡因子的更新
    • 插入——左单旋
    • 插入——右单旋
    • 插入——左右双旋
    • 插入——右左双旋
  • 四、ALVL树的验证
  • 五、AVL树的性能

一、AVL树的定义

AVL树,全称 平衡二叉搜索(排序)树

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

平衡因子(Balance Factor,简写为bf)
平衡因子(bf):结点的左子树的深度减去右子树的深度。也可以是右子树的深度减去左子树的深度。看个人实现而异。

即: 结点的平衡因子 = 左子树的高度 - 右子树的高度。
或者 节点的平衡因子 = 右子树的高度 - 左子树的高度。

AVL树本质上是一颗二叉查找树,但是它又具有以下特点:

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

这就是一颗AVL树
在这里插入图片描述

二、AVL树的作用

有一颗二叉树,他有n个节点,如果他是一颗二叉搜索树,他形状多样,可能会形成单枝树,高度为n,那么在这颗搜索树中查找元素的最坏时间复杂度为O(n),最好时间复杂度是O( l o g 2 n log_2 n log2n)。
如果他是一颗AVL树,他的高度稳定为 l o g 2 n log_2 n log2n,查找元素的时间复杂度为O( l o g 2 n log_2 n log2n)。在这里插入图片描述
由上图可知,同样的结点,由于插入方式不同导致树的高度也有所不同。特别是在带插入结点个数很多且正序的情况下,会导致二叉树的高度是O(N),而AVL树就不会出现这种情况,树的高度始终是O(lgN).高度越小,对树的一些基本操作的时间复杂度就会越小。这也就是我们引入AVL树的原因。

三、AVL树的插入操作

插入——平衡因子的更新

在插入一个元素的时候,必然会引起平衡因子的变化,所以我们需要在插入的时候把平衡因子同时更新,在平衡因子大于1或者小于-1时,我们则需要进行旋转操作,进行调整,使平衡因子再次正常,从而保证这颗二叉树一直是一颗AVL树。

使用平衡因子计算: 右子树高度 - 左子树高度

情况一:
在这里插入图片描述
在插入元素后,需要更新父节点的平衡因子,在父节点的左子树插入元素,父节点的平衡因子-1,在父节点的左子树插入元素,父节点的平衡因子+1,如果父节点的平衡因子更新过后变为1或者-1,则需继续往上更新至根节点,因为1或者-1表示该节点的高度发生改变,需往上更新。

情况2:
在这里插入图片描述
在插入元素后,需要更新父节点的平衡因子,在父节点的左子树插入元素,父节点的平衡因子-1,在父节点的左子树插入元素,父节点的平衡因子+1,如果父节点的平衡因子更新过后变为0,则不需要继续向上更新,因为变为0只能说明该树高度没有变化,只是相对于原来变得平衡。

如果在更新平衡因子后,平衡因子不在(-1/0/1)范围时,则需旋转操作,下面讲解如何进行旋转操作

由于插入需要旋转的情况较多,大致可以分为四大类

插入——左单旋

动图演示
请添加图片描述

情况一
右子树高时,在右子树的右侧插入元素,此时需要左单旋这里是引用

插入——右单旋

动图演示
请添加图片描述

情况二、
左子树较高时,在左子树的左侧插入元素,此时需要右单旋这里是引用

插入——左右双旋

情况三、左子树较高时,在左子树的右侧插入元素,此时需要左右双旋,即:先对30进行左单旋,然后再对90进行右单旋这里是引用

插入——右左双旋

情况四、右子树较高时,在右子树的左侧插入元素,此时需要右左双旋,即:先对90进行右单旋,然后再对30进行左单旋
在这里插入图片描述

四、ALVL树的验证

int _Height(Node* root)
{//用来计算二叉树的高度if (root == NULL)return 0;int leftH = _Height(root->_left);int rightH = _Height(root->_right);return leftH > rightH ? leftH + 1 : rightH + 1;
}bool _IsBalance(Node* root)
{if (root == NULL)return true;int leftH = _Height(root->_left);int rightH = _Height(root->_right);//检查平衡因子if (rightH - leftH != root->_bf){cout << root->_kv.first << "节点平衡因子异常" << endl;return false;}//通过计算左右子树的高度差判断这颗二叉树是否为AVL树return abs(leftH - rightH) < 2&& _IsBalance(root->_left)&& _IsBalance(root->_right);//检查高度差要检查二叉树中所有节点的左右子树的高度差
}bool IsBalance()
{return _IsBalance(_root);
}

五、AVL树的性能

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

但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

http://www.lryc.cn/news/129866.html

相关文章:

  • 「UG/NX」Block UI 面收集器FaceCollector
  • 剑指Offer61.扑克牌中的顺子 C++
  • vue实例挂载过程
  • 【第八讲---视觉里程计2】
  • 设置PHP的fpm的系统性能参数pm.max_children
  • vue3setup标签语法 + vite + delfin 递归组件实现无限评论功能
  • optee中如何开启或关闭所有中断的
  • 基于STM32+微信小程序设计的宠物投喂装置(腾讯云IOT)
  • 2023年上半年软考分数线 软考分数线公布时间
  • centos7的flink安装过程
  • 商城-学习整理-高级-性能压测缓存问题(十一)
  • PHP 三元 !empty 而不是评估为真或假 可用isset()
  • 星火大模型 VS FuncGPT(慧函数), 谁更胜一筹?
  • 使用 Python 获取 Redis 数据库中的所有键
  • C的进阶C++学习方向
  • 【仿写框架之仿写Tomact】二、初始化阶段加载项目中所有servlet类对象
  • Linux实用运维脚本分享
  • JMeter 特殊组件-逻辑控制器与BeanShell PreProcessor 使用示例
  • 时序预测 | MATLAB实现SA-ELM模拟退火算法优化极限学习机时间序列预测
  • Ubuntu 连接海康智能相机步骤(亲测,成功读码)
  • sass笔记
  • C/C++中volatile关键字详解
  • Linux:shell脚本:基础使用(4)《正则表达式-grep工具》
  • 如何建立单元测试
  • typeScript 接口和类
  • 这项与越来越多企业有关的行业标准,网易云信深度参与制定!
  • C语言,malloc使用规范
  • 广度优先遍历与最短路径(Java 实例代码源码包下载)
  • 南大通用数据库(Gbase 8s) 创建UDR外部函数
  • 步入React正殿 - State进阶