AVL树

AVL树的构造实现 今天接触的是平衡二叉查找树,对于AVL树来说相比之前的数据结构稍微多了一些需要注意的地方,照例在此总结总结让自己巩固一二。 首先是使用AVL树的动机: 我们知道AVL树本质上是排序二叉树的一类特例,回顾一下,对于排序二叉树的定义考虑使用归纳定义: ①、空树是二叉查找树; ②、若p和q都是二叉查找树,而root是一个“关键字大于p上所有结点的关键字,并小于q上所有结点的关键字”的元素 阅读详情

AVL树要解决的问题

为了尽量降低二叉搜索树的高度,减少树的平均搜索长度。

AVL树定义

或者是空树。
或者是具有下列性质的二叉搜索树:它的左子树和右子树都是AVL树,且左子树和右子树的高度之差的绝对值不超过1。

NOTE:
AVL树种左子树和右子树高度的差值称为平衡因子,平衡因子的值只能为-1、0和1,如果平衡因子的绝对值大于1,这棵二叉搜索树就失去了平衡,不再是AVL树。

AVL树的高度保持在O(lgn),平均搜索长度也可保持在O(lgn).

AVL树实现

要让AVL树时时刻刻保持平衡,就要在AVL树出现不平衡时及时恢复平衡。

AVL树的不平衡一共有四种情况:

这里写图片描述

只要解决这四种基本情况就行了。
首先给出AVL树抽象数据类型和结点类。

#include <iostream>
using namespace std;

//AVL树结点类
template <class Type>
class AVLNode
{
public:
    AVLNode() : left( NULL ), right( NULL ), balance( 0 ) {}
    AVLNode( Type d, AVLNode* l = NULL, AVLNode* r = NULL )
        : data( d ), left( l ), right( r ), balance( 0 ) {}

    Type data;
    AVLNode* left, *right;
    int balance;                //平衡因子
};

//AVL树定义
template <class Type>
class AVLTree
{
public:
    AVLTree() : root( NULL ), shorter( true ) {} //构造一棵空AVLTree
    AVLTree( Type ref ) : refValue( ref ), root( NULL ), shorter( true ) {}  //指定输入终止标志的AVLTree
    int Insert( Type x ) { int taller; return Insert( root, x, taller ); }
    Type Min();
    Type Max();
    int Depth() const;          //求树的高度
    void Remove(const Type& x) { Remove( x, root, shorter ); }

private:
    AVLNode<Type>* root;              //根节点的指针
    Type refValue;              //插入结束的标志
    int shorter;

protected:
    int Insert( AVLNode<Type>* &tree, Type x, int &taller );      //插入
    void RotateLeft( AVLNode<Type>* tree, AVLNode<Type>*& newTree );    //左单旋转
    void RotateRight( AVLNode<Type>* tree, AVLNode<Type>*& newTree );   //右单旋转
    void LeftBalance( AVLNode<Type>*& tree, int& taller );        //左平衡化
    void RightBalance( AVLNode<Type>*& tree, int& taller );       //右平衡化
    void Remove( const Type& x, AVLNode<Type>*& ptr, int& shorter );    //删除元素
    AVLNode<Type>* Min( AVLNode<Type>* ptr );
    AVLNode<Type>* Max( AVLNode<Type>* ptr );
    template <class T>
    friend istream& operator >>( istream& in, AVLTree<T>& tree );
    template <class T>
    friend ostream& operator <<( ostream& out, AVLTree<T>& tree );
    void traverse( AVLNode<Type>* ptr, ostream& out ) const;      //中序遍历
    int Depth( AVLNode<Type>* l ) const;//求树高度
};

这里写图片描述

/*
 * o
 *  o
 *   o   这种情况是左单旋转
 */
//右子树比左子树高,对以tree为根的AVL树做左单旋转,旋转后新根在newTree
template <class Type>
void AVLTree<Type>::RotateLeft( AVLNode<Type>* tree, AVLNode<Type>*& newTree )
{
    newTree = tree->right;          //新根为C
    tree->right = newTree->left;    //结点C的左子树转为结点A的右子树
    newTree->left = tree;           //结点A成为结点C的左子树
}

这里写图片描述

/*
 *    o
 *  o
 *o     这种情况需要右单旋转
 */
//左子树比右子树高,对以tree为根的AVL树做右单旋转,旋转后新根在newTree
//是左单旋转的镜象
template <class Type>
void AVLTree<Type>::RotateRight( AVLNode<Type>* tree, AVLNode<Type>*& newTree )
{
    newTree = tree->left;           //新根为B
    tree->left = newTree->right;    //结点B的右子树转为结点A的左子树
    newTree->right = tree;          //结点A成为结点B的右子树
}

这里写图片描述

/*
 *  o
 *o
 *  o   这种情况需要先左后右双旋转
 */
//
template <class Type>
void AVLTree<Type>::LeftBalance( AVLNode<Type>*& Tree, int& taller )
{
    AVLNode<Type>* rightsub, *leftsub = Tree->left;
    switch( leftsub->balance )
    {
        case -1:        //  '/'型  需要进行右单旋转
            Tree->balance = leftsub->balance = 0;           //右单旋转之后Tree和leftsub平衡因子都为0
            RotateRight( Tree, Tree );
            taller = 0;
            break;
        case 0:
            cout << "LeftBalance error : Tree already balanced.\n" << endl;
            break;
        case 1:         //  '<'型 需要进行先左后右双旋转
            rightsub = leftsub->right;      //设置调节完成之后的平衡因子
            switch( rightsub->balance )
            {
                case -1:
                    Tree->balance = 1;
                    leftsub->balance = 0;
                    break;
                case 0:
                    Tree->balance = leftsub->balance = 0;
                    break;
                case 1:
                    Tree->balance = 0;
                    leftsub->balance = -1;
                    break;
            }
            rightsub->balance = 0;
            RotateLeft( leftsub, Tree->left );
            RotateRight( Tree, Tree );
            taller = 0;
    }
}

这里写图片描述

/*
 *  o
 *    o
 *  o    这种情况需要先右后左双旋转
 */
template <class Type>
void AVLTree<Type>::RightBalance( AVLNode<Type>*& Tree, int& taller )
{
    AVLNode<Type>* rightsub = Tree->right, *leftsub;
    switch ( rightsub->balance )
    {
        case 1:
            Tree->balance = rightsub->balance = 0;
            RotateLeft( Tree, Tree );
            taller = 0;
            break;
        case 0:
            cout << "RightBalance error: Tree already balanced.\n" << endl;
            break;
        case -1:
            leftsub = rightsub->left;
            switch( leftsub->balance )
            {
                case 1:
                    Tree->balance = -1;
                    rightsub->balance = 0;
                    break;
                case 0:
                    Tree->balance = rightsub->balance = 0;
                    break;
                case -1:
                    Tree->balance = 0;
                    rightsub->balance = 1;
                    break;
            }
            leftsub->balance = 0;
            RotateRight( rightsub, Tree->right );
            RotateLeft( Tree, Tree );
            taller = 0;
    }
}

插入

//插入
//在以tree为根的AVL树中插入新元素x,如果插入成功,taller返回1,否则返回0
template <class Type>
int AVLTree<Type>::Insert( AVLNode<Type>*& tree, Type x, int &taller )
{
    int success;
    if ( tree == NULL )
    {
        tree = new AVLNode<Type>( x );
        success = (tree != NULL ? 1 : 0);
        if ( success )
            taller = 1;
    }
    else if ( x < tree->data )  //在左子树插入
    {
        success = Insert( tree->left, x, taller );
        if ( taller )
        {
            switch ( tree->balance )
            {
                case -1:
                    LeftBalance( tree, taller );
                    break;
                case 0:
                    tree->balance = -1;
                    break;
                case 1:
                    tree->balance = 0;
                    taller = 0;
                    break;
            }
        }
    }
    else                    //在右子树插入
    {
        success = Insert( tree->right, x, taller );
        if ( taller )
        {
            switch( tree->balance )
            {
                case -1:
                    tree->balance = 0;
                    taller = 0;
                    break;
                case 0:
                    tree->balance = 1;
                    break;
                case 1:
                    RightBalance( tree, taller );
                    break;
            }
        }
        return success;
    }
}

最大、最小值

template <class Type>
AVLNode<Type>* AVLTree<Type>::Min( AVLNode<Type>* ptr )
{
    if ( ptr == NULL )
        return NULL;
    while ( ptr->left != NULL )
    {
        ptr = ptr->left;
    }
    return ptr;
}

template <class Type>
AVLNode<Type>* AVLTree<Type>::Max( AVLNode<Type>* ptr )
{
    if ( ptr == NULL )
        return NULL;
    while ( ptr->right != NULL )
    {
        ptr = ptr->right;
    }
    return ptr;
}

template <class Type>
Type AVLTree<Type>::Min()
{
    AVLNode<Type>* temp;
    temp = Min( root );
    return temp == NULL ? refValue : temp->data;
}

template <class Type>
Type AVLTree<Type>::Max()
{
    AVLNode<Type>* temp;
    temp = Max( root );
    return temp == NULL ? refValue : temp->data;
}

删除

template <class Type>
void AVLTree<Type>::Remove( const Type& x, AVLNode<Type>*& ptr, int& shorter )
{
    int taller;
    if ( ptr == NULL )
        return ;
    if ( x == ptr->data )
    {
        //情况1:如果被删元素有两个子女,将被删元素转移到子女上,将情况转换为2
        if ( ptr->left != NULL && ptr->right != NULL )
        {
            AVLNode<Type>* leftMax = Max( ptr->left );  //左孩子中最大的
            ptr->data = leftMax->data;                  //中序遍历直接前驱的值赋给要删结点
            Remove( leftMax->data, ptr->left, shorter ); //在左子树中删除中序遍历中的直接前驱
        }
        //如果被删元素最多有一个子女,删除结点,修改父结点指向
        else
        {
            //被删结点两个子女都为空,直接删除
            if ( ptr->left == NULL && ptr->right == NULL )
            {
                delete ptr;
                ptr = NULL;
            }
            //被删结点的左子女为空,将右子女赋值给要删结点。
            else if ( ptr->left == NULL )
            {
                AVLNode<Type>* temp = ptr;
                ptr = temp->right;
                delete temp;
                shorter = true;
            }
            //被删结点的右子女为空,将左子女赋值给要删结点。
            else
            {
                AVLNode<Type>* temp = ptr;
                ptr = temp->left;
                delete temp;
                shorter = true;
            }
        }
    }
    else if ( x < ptr->data )
    {
        Remove( x, ptr->left, shorter );
        if ( shorter )
        {
            switch ( ptr->balance )
            {
                case 1:
                    ptr->balance = 0;
                    shorter = true;
                    break;
                case 0:
                    ptr->balance = -1;
                    shorter = false;
                    break;
                case -1:
                    RightBalance( ptr, taller );
                    if ( ptr->right->balance == 0 )
                        shorter = false;
                    else
                        shorter = true;
                    break;
            }
        }
    }
    else
    {
        Remove( x, ptr->right, shorter );
        if ( shorter )
        {
            switch ( ptr->balance )
            {
                case 1:
                    LeftBalance( ptr, taller );
                    if ( ptr->left->balance = 0 )
                        shorter = false;
                    else
                        shorter = true;
                    break;
                case 0:
                    ptr->balance = 1;
                    shorter = false;
                    break;
                case -1:
                    ptr->balance = 0;
                    shorter = true;
                    break;
            }
        }
    }
}

输入输出

template <class Type>
istream& operator >>( istream& in, AVLTree<Type> &tree )
{
    Type item;
    cout << "Construct AVL tree:\n";
    cout << " Input Data( end with " << tree.refValue << " ): ";
    in >> item;
    while ( item != tree.refValue )
    {
        tree.Insert( item );
        in >> item;
    }
    return in;
}

template <class Type>
ostream& operator <<( ostream& out, AVLTree<Type>& tree )
{
    cout << "Inorder traversal of AVL tree.\n";
    tree.traverse( tree.root, out );
    return out;
}

//中序遍历
template <class Type>
void AVLTree<Type>::traverse( AVLNode<Type>* ptr, ostream& out ) const
{
    if ( ptr != NULL )
    {
        traverse( ptr->left, out );
        out << ptr->data << " " ;
        traverse( ptr->right, out );
    }
}

测试

int main( void )
{
    AVLTree<int> tree(0);
    cin >> tree;
    cout << tree << endl;
    tree.Remove( 7 );
    cout << tree << endl;
    cout << tree.Min() << endl;
    cout << tree.Max() << endl;
    return 0;
}
优惠券管理 (请设计一个优惠券兑换码生成方案,可以支持20亿以上的唯一兑换码,兑换码长度不超过10,只能包含字母数字,并且要保证生成和校验算法的高效)最后把签名值的后14位、新鲜值(4位)、自增序列(32位)拼接,得到一个50位二进制数,然后与一个较大的质数做异或运算加以混淆,再基于Base32或Base64转码,即可的对兑换码。现在,就剩下防止爆刷了。自增id从1增加到Integer的最大值,可以达到40亿以上个数字,而占用的字节仅仅4个字节,也就是32个bit位,距离50个bit位的限制还有很大的剩余,符合要求! 阅读详情

相关推荐

【SAP PO】SAP PO 接口配置完整教程之二REST服务对接

SAP PO 接口配置完整教程之二REST服务对接

XLevon的博客 1万+

C++AVL树

AVL树零、前言一、AVL树的概念二、AVL树结点定义三、AVL树的插入四、AVL树的旋转1、左单旋2、右单旋3、左右双旋4、右左双旋5、总结五、AVL树的验证六、AVL树的性能 零、前言 本章主要讲解map和set的底层结构平衡二叉搜索树的一种-AVL树的特性及其实现 一、AVL树的概念 引入: map/multimap/set/multiset其底层都是按照二叉搜索树来实现的,但是二叉搜索树有其自身的缺陷 假如往树中插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退

~ 2821

Abaqus仿真进阶:如何准确计算气动软体机器人的抓取力(Blocking Force分析)

本文详细介绍了使用Abaqus进行气动软体机器人抓取力(Blocking Force)分析的进阶技巧。从材料模型验证、刚性目标物选择到接触对建立和充气加载优化,提供了一套完整的有限元仿真解决方案,帮助工程师准确预测软体执行器的抓取性能。

weixin_42528902的博客 401

AVL树的旋转

文章目录定义AVL树的旋转左单旋转右单旋转双旋转 定义         一棵AVL树可以是空树,也可以是具有下列性质的二叉搜索树:它的左子树和右子树都是AVL树,且左子树和右子树的高度之差的绝对值不超过1(也就是小于等于1)。(右子树高度 - 左子树高度)         节点的平衡因子:给每个节点附加一个数字,给出该节点右子树的高度减去左子树的高度

Colorful_X的博客 4894

国内免魔法接入Grok API,打造智能QQ机器人实战指南

大型语言模型(LLM)通过API接口为开发者提供了强大的自然语言处理能力,其核心原理是基于海量数据训练的深度神经网络,能够理解和生成类人文本。这一技术价值在于显著降低了AI应用开发门槛,使开发者无需从零训练模型即可快速集成智能对话、内容生成等功能。在即时通讯、智能客服、自动化工具等应用场景中,LLM API能有效提升交互体验与效率。本文聚焦于如何通过稳定的第三方API服务,绕开网络访问限制,将xAI推出的Grok模型接入国内网络环境,并结合NoneBot2框架与go-cqhttp,实现一个功能完整的智能QQ

weixin_30607125的博客 331

Clock Gating设计陷阱:为什么你的ICG cell总是导致setup violation?(附解决方案)

本文深入分析了数字IC设计中ICG(Integrated Clock Gating)单元导致setup时序违规的常见问题,包括时钟偏斜和数据路径过长两大核心原因。通过量化分析影响因素,提供了逻辑重组、寄存器重定时等优化策略,并介绍了预防性设计方法和物理实现阶段的优化技巧,帮助工程师有效解决ICG相关的setup violation问题。

weixin_29168153的博客 206

【C++】AVL树

本篇内容到这里就结束了,主要讲了AVL树的定义与主要接口实现,希望对大家有帮助,祝生活愉快,我们下一篇再见!

春风不语。 2040

“傻瓜”stata入门——熟悉操作界面

小白开始学习stata和matlab,零基础上手

nnpersistent的博客 4459

C++之AVL树的介绍以及AVL树自我实现

AVL树是一种自平衡的二叉查找树,它的名字来源于它的发明者G.M. Adelson-Velsky和E.M. Landis。AVL树是最先发明的⾃平衡⼆叉查找树,AVL是⼀颗空树,或者具备下列性质的⼆叉搜索树:它的左右⼦树都是AVL树,且左右⼦树的⾼度差的绝对值不超过1。AVL树是⼀颗⾼度平衡搜索⼆叉树,通过控制⾼度差去控制平衡。

zzh_Zao的博客 1819

基于Transformer的CFD代理模型:AeroTransformer原理、实现与优化

计算流体动力学(CFD)仿真是航空航天、汽车工程等领域的关键技术,但其高计算成本限制了设计优化效率。代理模型通过机器学习方法学习输入参数与流场输出间的映射关系,能实现秒级预测,大幅加速仿真流程。Transformer架构凭借其全局注意力机制,能有效建模流场中的长程物理关联,克服了传统卷积神经网络在捕捉全局依赖上的局限。通过预训练与微调策略,模型能先在大规模多样化数据集上学习通用流体规律,再针对特定任务快速适配,显著提升了小样本下的泛化能力与工程实用价值。AeroTransformer项目正是这一技术路线的典

weixin_33509203的博客 360

FPGA实现PID算法

PID 控制器表达式如下(引用某硕士论文 《基于FPGA的PID控制器研究与实现》): 其中,Kp为比例增益,Ti为积分时间常数,Td为微分时间常数,u(t)为控制量,e(t)为偏差。 在数字控制系统中,PID控制规律的实现必须用数值逼近的方法。当采样周期相当短时,用求和代替积分、用后向差分代替微分,使模拟PID离散化变为差分方程。 本文使用并行结构来实现。具有延时小,计算速度快的特点,但需要资源比较多。 利用图3.2所示并行结构实现方法 ,进一步将3.8式分解: ...

nxin168的博客 1万+

AVL树的删除

引言 AVL树的定义及性质及其重要的插入方法,我已在AVL树的插入一文中详细阐述过了,其中重要的旋转也已进行详细描述。 本文就不再赘述了,直接使用,要是不清楚的小伙伴,还请移步AVL树的插入再回头复习一下下。 AVL树的删除 AVL树的删除步骤如下: 寻找删除目标(存不存在,定位出目标) 判断删除目标的类型,使用转换的思路,将其转化为删除叶子 将删除目标与AVL树链接断开,调整平衡,完成删除 寻找删除目标 寻找删除目标的思路很简单,因AVL树也属于BST,因此寻找目标的做法与BST的删除思路基本一致。

qq_39208237的博客 5889

uniapp [全端兼容] - 实现接入使用天地图及卫星视图切换功能,在国家天地图上自由切换地图显示类型(卫星视图/地形视图/卫星混合/地形混合/普通视图)天地图切换视图显示改成卫星风格,切换地图图层

uni-app,h5移动端网页,微信小程序,支付宝小程序,Android安卓,苹果ios,app,nvue,天地图,视图,卫星,地图类型切换,uniAPP天地图,改成卫星视角,在线地图切换,来回切换卫星图,天地图如何实现来回切换卫星图,地形图,卫星视图,VUE3引入天地图卫星版,详细教程示例源码代码,天地图切换卫星用什么api,卫星视角API方法,vue3卫星天地图,能改地图的显示方式,vue3中使用天地图自定义样式切换地图图层,切换报错,加载出错,卫星图太卡,地图图层更换,vue3安装集成天地图详细示例源

🏆 前端领域优质创作者 1万+

AVL树:AVL树的实现

pair是将2个数据组合成一组数据,如stl中的map就是将key和value放在一起来保存,或者当一个函数需要返回2个数据的时候,也可以选择pair。pair的实现是一个结构体,主要的两个成员变量first和second,分别存储两个数据, 因为是使用struct不是class,所以可以直接使用pair的成员变量。//_pf记录高度差(右子树高度减去左子树高度)int _pf;

2301_80468112的博客 837

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

C++——AVL树

AVL树的简介

二哈的博客 1719

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

【AVL树】

AVL树

yzh的博客 939

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

AVL树详解

AVL树,又称为平衡二叉树,它基于二叉搜索树并通过平衡而得到。在数据结构中,二叉搜索树可以提高搜索数据的效率,但在数据有序的情况下会退化为单支树,此时在树中查找元素就得遍历一整个分支,时间复杂度也会退化至O(N)。AVL树的左右子树高度差不能超过1,也就是说每个节点的平衡因子的绝对值不能超过1;, _bf(0){}//节点的父节点T _data;//节点的值int _bf;// 节点的平衡因子。

w8848w的博客 941

AVL树的插入

为什么需要AVL树? 我们知道搜索二叉树通过对每一个结点限制规定:左子树都比比根节点小,右子树比根节点大,来达到O(logN)的搜索效率的:​​ 但是一般的搜索二叉树还有一个很严重的问题:当左右子树高度差很大时,会导致搜索效率极度退化,甚至在顺序插入时退化至O(N): 很明显,普通的搜索二叉树在一些情况下并不能达到预期的O(logN)搜索效率,而其原因就是高度的极度不平衡。 而AVL树就通过旋转的方式来维持搜索二叉树的平衡,AVL树得名于它的发明者G. M. Adelson-Vels

LiangXiay的博客 2831

树——AVL树

文章目录AVL树(BST树的优化和改进)AVL树代码 AVL树(BST树的优化和改进) BST树最差的情况下,就退化成一条链表了,增删查的时间复杂度就无法达到O(log⁡2n\log_2nlog2​n) ,AVL树在BST的基础上,加入了节点左右子树高度差的概念,就是任意一个节点的左子树和右子树的高度差不能超过1,就说这颗树是一颗平衡树,否则就要通过既定规则的旋转操作,使排序树达到平衡的状态。 A...

中都 739

C++实现AVL树

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

m0_53421868的博客 2277
上一篇: 二叉搜索树
下一篇: 红黑树
Findway_
博客等级 码龄10年 36粉丝 117原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值