【C++进阶】AVL树的实现
创始人
2025-05-30 19:30:55

文章目录

  • AVL树概念
  • AVL树性质
  • AVL树节点的结构
  • AVL树的插入
    • 插入新节点
    • 更新整体节点的平衡因子
    • 旋转处理
      • 右单旋
      • 左单旋
      • 左右双旋
      • 右左双旋
  • AVL树的整体代码

AVL树概念

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

AVL树性质

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

两种计算平衡因子的方法
法一:节点的左子树高度减右子树高度
在这里插入图片描述
法二:节点的右子树高度减左子树高度
在这里插入图片描述

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在
O(log2n)O(log_2 n)O(log2​n),搜索时间复杂度O(log2nlog_2 nlog2​n)。

AVL树节点的结构

实现的是KV模型
平衡因子的计算方法由第二种方法实现
在这里插入图片描述

AVL树的插入

分为三个步骤
1.按照二叉搜索树的方式插入新节点
2.更新整体节点的平衡因子
3.更新平衡因子的过程中判断是否需要旋转

插入新节点

bool Insert(const pair& kv)
{Node* parent = nullptr;Node* cur = _root;//如果根为空if (_root == nullptr){_root = new Node(kv);return true;}//寻找插入位置while (cur){//如果要插入的值比cur的值小if (cur->_kv.first > kv.first){//往左找parent = cur;cur = cur->_left;}//如果要插入的值比cur的值大else if (cur->_kv.first < kv.first){//往右找parent = cur;cur = cur->_right;}//如果相等就不再插入else{return false;}}//插入Node* newnode = new Node(kv);cur = newnode;if (parent->_kv.first > kv.first){parent->_left = newnode;newnode->_parent = parent;//父母节点更新平衡因子parent->_bf--;}else{parent->_right = newnode;newnode->_parent = parent;//父母节点更新平衡因子parent->_bf++;}
}

更新整体节点的平衡因子

更新整体节点的平衡因子过程中分三种情况

1.首先在插入的过程中,更新插入节点的父母节点的平衡因子
在这里插入图片描述


2.更新以后判断父母节点的平衡因子
1)更新后父母节点的平衡因子是0
停止更新,说明更新前父母节点的平衡因子是1或者-1,现在变成0也表明了父母节点的左右子树中高度矮的子树插入了新节点
在这里插入图片描述

if (parent->_bf == 0){break;}

(2)更新后父母节点的平衡因子是1或者-1
继续向上更新, 说明更新前父母节点的平衡因子是0,现在是1或者-1,也表明父母节点的左右子树中的一颗树的高度变高了。父母节点所在的子树高度也变高了。
在这里插入图片描述
在这里插入图片描述

else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;//继续向上更新parent = parent->_parent;if (parent){if (parent->_left == cur){parent->_bf--;}else{parent->_bf++;}}}

(3)更新后父母节点的平衡因子是2或者-2
说明父母节点所在的子树已经不平衡了需要旋转处理

在这里插入图片描述

else if (parent->_bf == 2 || parent->_bf == -2){if (parent->_bf == -2 && cur->_bf == -1){//右单旋RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == 1){//左单旋RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){//左右双旋RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){//右左双旋RotateRL(parent);}else{//不可能的情况assert(false);}break;}

整体的插入代码

//更新整体平衡因子
while (parent)
{if (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;//继续向上更新parent = parent->_parent;if (parent){if (parent->_left == cur){parent->_bf--;}else{parent->_bf++;}}}else if (parent->_bf == 2 || parent->_bf == -2){if (parent->_bf == -2 && cur->_bf == -1){//右单旋RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == 1){//左单旋RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){//左右双旋RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){//右左双旋RotateRL(parent);}else{//不可能的情况assert(false);}break;}else{//不可能的情况assert(false);}
}

旋转处理

旋转处理是为了处理保证二叉搜索树处于平衡状态,每当二叉搜索树不平衡时就会触发旋转。
旋转又分4种分别处理4种不平衡的情况。

右单旋

在这里插入图片描述
在这里插入图片描述

void RotateR(Node* parent)
{//要调整的节点Node* sub = parent;//要调整的节点的左孩子Node* subL = parent->_left;//要调整的节点的左孩子的右孩子Node* subLR = subL->_right;//要调整的节点的父母Node* subparent = sub->_parent;//重新链接关系if (subLR)subLR->_parent = sub;sub->_left = subLR;sub->_parent = subL;subL->_right = sub;subL->_parent = subparent;if (_root == sub){_root = subL;}else{if (subparent->_left == sub){subparent->_left = subL;}else{subparent->_right = subL;}}subL->_bf = sub->_bf = 0;
}

左单旋

在这里插入图片描述

void RotateL(Node* parent)
{//要调整的节点Node* sub = parent;//要调整的节点的右孩子Node* subR = parent->_right;//要调整的节点的有孩子的左孩子Node* subRL = subR->_left;//要调整的节点的父母Node* subparent = sub->_parent;//重新链接关系if (subRL)subRL->_parent = sub;sub->_right = subRL;sub->_parent = subR;subR->_left = sub;subR->_parent = subparent;if (_root == sub){_root = subR;}else{if (subparent->_left == sub){subparent->_left = subR;}else{subparent->_right = subR;}}subR->_bf = sub->_bf = 0;
}

左右双旋

在这里插入图片描述

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

右左双旋

在这里插入图片描述

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

AVL树的整体代码

AVL.h

#pragma once
#include
namespace lzf
{templatestruct AVLTreeNode{AVLTreeNode* _left;//该节点的左孩子AVLTreeNode* _right;//该节点的右孩子AVLTreeNode* _parent;//该节点的双亲节点pair _kv;//该节点的kvint _bf;//该节点的平衡因子//构造函数AVLTreeNode(const pair& kv):_left(nullptr),_right(nullptr),_parent(nullptr),_bf(0),_kv(kv){}};templateclass AVLTree{typedef AVLTreeNode Node;public:AVLTree():_root(nullptr){}bool Insert(const pair& kv){Node* parent = nullptr;Node* cur = _root;//如果根为空if (_root == nullptr){_root = new Node(kv);return true;}//寻找插入位置while (cur){//如果要插入的值比cur的值小if (cur->_kv.first > kv.first){//往左找parent = cur;cur = cur->_left;}//如果要插入的值比cur的值大else if (cur->_kv.first < kv.first){//往右找parent = cur;cur = cur->_right;}//如果相等就不再插入else{return false;}}//插入Node* newnode = new Node(kv);cur = newnode;if (parent->_kv.first > kv.first){parent->_left = newnode;newnode->_parent = parent;//父母节点更新平衡因子parent->_bf--;}else{parent->_right = newnode;newnode->_parent = parent;//父母节点更新平衡因子parent->_bf++;}//更新整体平衡因子while (parent){if (parent->_bf == 0){break;}else if (parent->_bf == 1 || parent->_bf == -1){cur = parent;//继续向上更新parent = parent->_parent;if (parent){if (parent->_left == cur){parent->_bf--;}else{parent->_bf++;}}}else if (parent->_bf == 2 || parent->_bf == -2){if (parent->_bf == -2 && cur->_bf == -1){//右单旋RotateR(parent);}else if (parent->_bf == 2 && cur->_bf == 1){//左单旋RotateL(parent);}else if (parent->_bf == -2 && cur->_bf == 1){//左右双旋RotateLR(parent);}else if (parent->_bf == 2 && cur->_bf == -1){//右左双旋RotateRL(parent);}else{//不可能的情况assert(false);}break;}else{//不可能的情况assert(false);}}return true;}void InOrder(){_InOrder(_root);}bool IsbalanceTree(){return _IsbalanceTree(_root);}private:void RotateR(Node* parent){//要调整的节点Node* sub = parent;//要调整的节点的左孩子Node* subL = parent->_left;//要调整的节点的左孩子的右孩子Node* subLR = subL->_right;//要调整的节点的父母Node* subparent = sub->_parent;//重新链接关系if (subLR)subLR->_parent = sub;sub->_left = subLR;sub->_parent = subL;subL->_right = sub;subL->_parent = subparent;if (_root == sub){_root = subL;}else{if (subparent->_left == sub){subparent->_left = subL;}else{subparent->_right = subL;}}subL->_bf = sub->_bf = 0;}void RotateL(Node* parent){//要调整的节点Node* sub = parent;//要调整的节点的右孩子Node* subR = parent->_right;//要调整的节点的有孩子的左孩子Node* subRL = subR->_left;//要调整的节点的父母Node* subparent = sub->_parent;//重新链接关系if (subRL)subRL->_parent = sub;sub->_right = subRL;sub->_parent = subR;subR->_left = sub;subR->_parent = subparent;if (_root == sub){_root = subR;}else{if (subparent->_left == sub){subparent->_left = subR;}else{subparent->_right = subR;}}subR->_bf = sub->_bf = 0;}void RotateRL(Node* parent){Node* subR = parent->_right;Node* subRL = subR->_left;int bf = subRL->_bf;RotateR(parent->_right);RotateL(parent);if (bf == -1){parent->_bf = 0;subR->_bf = 1;subRL->_bf = 0;}else if (bf == 1){parent->_bf = -1;subRL->_bf = 0;subR->_bf = 0;}else if (bf == 0){parent->_bf = 0;subRL->_bf = 0;subR->_bf = 0;}else{assert(false);}}void RotateLR(Node* parent){Node* subL = parent->_left;Node* subLR = subL->_right;int bf = subLR->_bf;RotateL(parent->_left);RotateR(parent);if (bf == -1){parent->_bf = 1;subL->_bf = 0;subLR->_bf = 0;}else if (bf == 1){parent->_bf = 0;subLR->_bf = 0;subL->_bf = -1;}else if (bf == 0){parent->_bf = 0;subLR->_bf = 0;subL->_bf = 0;}else{assert(false);}}void _InOrder(Node* root){if (root == nullptr){return;}_InOrder(root->_left);cout << root->_kv.first << ":" << root->_kv.second << endl;_InOrder(root->_right);}int Hegiht(Node* root){if (root == nullptr){return 0;}return Hegiht(root->_left) > Hegiht(root->_right) ? Hegiht(root->_left) + 1 : Hegiht(root->_right) + 1;}bool _IsbalanceTree(Node* root){if (root == nullptr){return true;}int leftHeight = Hegiht(root->_left);int rightHeight = Hegiht(root->_right);if ((rightHeight - leftHeight) != root->_bf){cout << root->_kv.first << "现在bf是:" << root->_bf <<" ";cout << "应该是:" << (rightHeight - leftHeight) << endl;return false;}return abs(rightHeight - leftHeight) < 2&& _IsbalanceTree(root->_left)&& _IsbalanceTree(root->_right);}private:Node* _root;};void Test_AVLTree1(){AVLTree tree;tree.Insert(make_pair(5, 5));tree.Insert(make_pair(3, 3));tree.Insert(make_pair(8, 8));tree.Insert(make_pair(4, 4));tree.Insert(make_pair(6, 6));tree.Insert(make_pair(2, 2));tree.Insert(make_pair(7, 7));tree.InOrder();cout << tree.IsbalanceTree();}void Test_AVLTree2(){AVLTree t;//int a[] = {5,4,3,2,1,0};//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };for (auto e : a){t.Insert(make_pair(e, e));cout << "Insert" << e << ":" << t.IsbalanceTree() << endl;}t.InOrder();cout << t.IsbalanceTree() << endl;}}

相关内容

热门资讯

【前端八股文】JavaScri... 文章目录Set概念与arr的比较属性和方法并集、交集、差集Map概念属性和方法String用索引值和...
海康硬盘录像机接入RTSP/o... EasyNVR安防视频云服务平台可支持设备通过RTSP/Onvif协议接入平台,能提供...
在混合劳动力时代如何避免网络安... 在混合劳动力时代如何避免安全网络风险 三年多来,混合工作一直是工作生活中不可或缺的一...
2023还不懂Jmeter接口... 这里介绍的Jmeter接口测试的的实战,如果文章内容没遇看懂的话,我这边...
基于4G/5G弱网聚合的多链路... 基于4G/5G多卡聚合(弱网聚合)的智能融合通信设备技术亮点 增强带宽提供可靠连接 通过将多个有线和...
如何使用Synplify综合v... 文章目录使用Synplify综合的好处synplify的教程方法1(无效)...
2023年全国最新高校辅导员精... 百分百题库提供高校辅导员考试试题、辅导员考试预测题、高校辅导员考试真题、辅导员证考试题库等ÿ...
2022年18个值得期待的Le... 有数百个独特的LearnDash附加组件,您可能很难选择您的LearnDash LMS...
【java基础】Stream流... 文章目录基本介绍流的创建流的各种常见操作forEach方法filter方法map方法peek方法fl...
javaweb高校行政办公自动... 本课题基于我国高校管理信息化建设现状,结合在实际工作中所遇到的问题和收获,...
一款专门为自动化测试打造的集成... 你好,我是不二。 随着行业内卷越来越严重,自动化测试已成为测试工程师的...
【go-zero】golang... 一、casbin 概览 1、casbin基本了解 casbin的GitHub:https://git...
现在开发低代码平台算晚吗? 现在开发低代码平台算晚吗?作为低代码的亲戚——零代码厂商,这篇就以“厂商...
【JavaWeb】书城项目(2... 222.书城项目-第三阶段:修改所有html页面为jsp页面 改成jsp页面之后&#x...
基于jeecgboot的大屏设...      通过前面设计好数据源后,就要进行数据集的设计了。      一、还是在onl...
Linux命令小技巧:显示文件... 工作中会有很多千奇百怪的需求,比如:如何在 Linux 命令行中快速找到...
【找工作】-- 大数据工程师找... 目录 1.前言 2.找工作的理论知识 2.1 分析个人特征 2.1.1 你自身优势是什么?
C++基础算法④——排序算法(... 排序算法 1.插入排序 2.桶排序 1.插入排序 基本思想:将初始数据分为有序部分和...
nginx快速入门.跟学B站n... nginx快速入门.跟学B站nginx一小时精讲课程笔记nginx简介及环境准备nginx简介环境准...
ORACLE存过互相调用之间事... 今天在问答区看到一个问题是 假如有procedureA、procedureB和procedureC&...
基于java中Springbo... 基于java中Springboot框影视影院订票选座管理系统 开发语言:Java 框...
CVE-2018-18086 最近闲来无事,看到青少年CTF平台,感觉对新手还是比较友好的࿰...
【深度学习】基于Hough变化... 💥💥💞💞欢迎来到本博客❤️❤️&#x...
AtCoder Beginne... D - Bank (atcoder.jp)         (1)题目大...
VxWorkds 内存管理(3... 虚拟内存管理 对于带MMU的目标板,VxWorks提供虚拟内存的支持,V...
【微服务】—— 统一网关Gat... 文章目录1. 概述1.1 为什么需要网关1.2 SpringCloud Gateway2. gate...
文心一言实际测试——让我们拿实... 文心一言实际测试——让我们拿实际说好坏 4月16日,文心一言闪亮登场,就...
Postgresql源码(10... 0 总结 (可以最后看) PLpgSQL_execstate中包含的两个...
网络技术与应用概论(上)——“... 各位CSDN的uu们你们好呀,今天,小雅兰的内容依旧是计算机网络的一些知...
Yolov5 QAT量化训练 1. QAT介绍 从 模型量化(5): 敏感层分析可以看出来,对于yolov5-nano模型,对最后...