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

【C++】AVL树

目录

一、AVL树的引入

 二、AVL树

🍔AVL树的概念

🍟AVL树节点的定义

🌮AVL树的插入

🥪AVL树的旋转

三、AVL树的验证

 四、结语


一、AVL树的引入

🌟我们知道 map/multimap/set/multiset 这几个容器的共同点是:其底层都是按照二叉搜索树来实现

不过二叉搜索树有其自身的缺陷,假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成O(N),因此 map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现

💧接下来我们就一起看看如何实现平衡二叉树的改造:



 二、AVL树

🍔AVL树的概念

🌟针对效率降低的问题,两位俄罗斯的数学家G.M.Adelson-Velskii 和E.M.Landis在1962年发明了一种解决的方法:

👉当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1,即可降低树的高度,从而减少平均搜索长度。

AVL树是具有以下性质的二叉搜索树:

1️⃣它的左右子树都是AVL树

2️⃣左右子树高度之差(简称平衡因子)的绝对值不超过1(需要通过调整系欸但

🍟AVL树节点的定义

🌟AVL树节点的定义:

🌮AVL树的插入

AVL树的插入分为两步:

1️⃣按照二叉搜索树的方式插入新节点

2️⃣调整节点的平衡因子

针对平衡因子的调整:

设平衡因子的计算:右子树高度-左子树高度,所以对于一个节点来说:

1️⃣新节点插入到左边,平衡因子 -1

2️⃣新节点插入到右边,平衡因子 +1


插入后,节点的平衡因子可能的情况 0,正负1,正负2

0:插入之前为正负1,插入后变为0

正负1:插入之前为0,插入后整体的高度变高一层,需要向上更新

正负2:插入之前为正负1,插入后违反了平衡树的性质,需要进行节点位置的调制

bool insert(const T& data)
{//按照二叉搜索树的插入规则插入//判断_root是否为空if (!_root)_root = new Node(data);pNode cur = _root;pNode parent = nullptr;while (cur){if (cur->_data > data)cur = cur->_pleft;else if (cur->_data < data)cur = cur->_pright;elsereturn false;}pNode newNode = new Node(data);if (data > parent->_data){parent->_pright = newNode;newNode->_parent = parent;}else{parent->_pleft = newNode;newNode->_parent = parent;}//调整平衡因子while (parent){// 更新双亲的平衡因子if (newNode == parent->_pleft)parent->_bf--;elseparent->_bf++;// 更新后检测双亲的平衡因子if (0 == parent->_bf){break;}else if (1 == parent->_bf || -1 == parent->_bf){newNode = parent;parent = newNode->_parent;}else{if (2 == parent->_bf){// ...}else{// ...}}}return true;
}

🥪AVL树的旋转

❗针对平衡因子出现正负2的情况,我们需要进行旋转处理

🌟下面分别讨论不同情况下的旋转:


1️⃣新节点插入较高左子树的左侧---左左 : 右单旋


2️⃣新节点插入较高右子树的右侧--右右:左单旋


3️⃣新节点插入较高左子树的右侧---左右:先左单旋再右单旋


4️⃣新节点插入较高右子树的左侧---右左:先右单旋再左单旋


🔥总结:

假如以parent为根的子树不平衡,即parent的平衡因子为2或者-2,分以下情况考虑

1️⃣parent的平衡因子为2,说明parent的右子树高,设parent的右子树的根为subR

当subR的平衡因子为1时,执行左单旋

当subR的平衡因子为-1时,执行右左双旋

2️⃣parent的平衡因子为-2,说明pParent的左子树高,设parent的左子树的根为subL

当subL的平衡因子为-1时,执行右单旋

当subL的平衡因子为1时,执行左右双旋

旋转完成后,原parent为根的子树已经平衡,不需要再向上更新


三、AVL树的验证

🌟AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:

1️⃣验证其为二叉搜索树,如果中序遍历可得到一个有序的序列,就说明为二叉搜索树

2️⃣验证其为平衡树,每个节点子树高度差的绝对值不超过1


提供两个代码供大家验证:

void test1()
{const int N = 30;vector<int> v;v.reserve(N);//srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand());cout << v.back() << endl;}AVLTree<int> t;for (auto e : v){if (e == 14604){int x = 0;}t.insert(e);cout << "Insert:" << e << "->" << t.IsBalance() << endl;}cout << t.IsBalance() << endl;t.InOrder();
}
void test2()
{//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };AVLTree<int> t;for (auto e : a){t.insert(e);}int i = 0;t.InOrder();cout << t.IsBalance() << endl;return 0;
}


 四、结语

🫡你的点赞和关注是作者前进的动力!

🌞最后,作者主页有许多有趣的知识,欢迎大家关注作者,作者会持续更新有意思的代码,在有趣的玩意儿中成长!

http://www.xdnf.cn/news/107227.html

相关文章:

  • 《从卷积核到数字解码:CNN 手写数字识别实战解析》
  • 蚊子的搜索距离可达60公里:对一些特殊气味有所偏爱
  • 短说社区V5.2.1正式版发布|修复已知问题
  • 品牌名凭空消失?3步破解亚马逊前台标题隐藏危机
  • 在Linux驱动开发中使用DeepSeek的方法
  • 智能指针(shared_ptr)之二
  • 18487.1-2015-解读笔记五-交流充电之停止充电
  • 详解 synchronized 关键字【通俗易懂】
  • 前端常见问题
  • 西门子S7-200SMART 控制Profinet闭环步进MD-4250-PN (1)电机及专栏介绍
  • 基于百度地图 MCP Server规划规划一次青岛到北京旅行的详细行程实践
  • Vue3集成百度实时语音识别
  • 【Redis】集合类型Set 常用命令详解
  • ZLMediaKit支持JT1078实时音视频
  • 新手村:正则化
  • 系统架构师2025年论文《系统架构风格》
  • Airflow全局异常捕获实现消息通知实践
  • LeetCode-46. 全排列
  • 洛谷P3196C语言题解
  • PHP CURL发送POST请求(支持HEADER参数配置)
  • Kubernetes 集群内访问外部服务的三种实践方案
  • 软件工程的13条“定律”:从Hyrum定律到康威定律,再到Zawinski定律
  • 锤子线,买入准确概率是多少
  • leetcode-数组
  • Retrofit框架分析(二):注解、反射以及动态代理,Retrofit框架动态代理的源码分析
  • bert学习
  • AIGC的伦理困境:机器生成内容是否该被监管?
  • 动态脚本引擎QLExpress,实现各种复杂的业务规则
  • 深度学习驱动的车牌识别:技术演进与未来挑战
  • 创建第一个Spring Boot项目