数据结构 - AVL树 (Adelson-Velsky and Landis Tree)

news2025/7/19 12:17:08

目录

  • 一、前言
  • 二、简介
  • 三、左旋与右旋
  • 四、AVL树的调整
    • 1、向AVL树中插入新数据
      • 1)LL型不平衡(右单旋转)
      • 2)RR型不平衡(左单旋转)
      • 3)LR型不平衡(左右双旋转)
      • 4)RL型不平衡(右左双旋转)
  • 五、代码实现

一、前言

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序,二叉搜索树将有可能退化为单支树,此时查找元素相当于在顺序表中查找元素,效率低下,时间复杂度接近O(n)。请添加图片描述
最优情况下,二叉搜索树为完全二叉树。
最差情况下,二叉搜索树退化为单支树。

二、简介

AVL树是一颗自平衡的二叉树,是对BST的改进。每个节点均有一个平衡因子 p p p p = H e i g h t ( T L ) − H e i g h t ( T R ) p=Height(T_L)-Height(T_R) p=Height(TL)Height(TR))。若每个节点的平衡因子的绝对值均小于等于1,则该树是AVL树。维护这种高度平衡所付出的代价也是很大的,故而实际应用并不多,因此更多的地方是用追求局部平衡而不是整体平衡的红黑树。
请添加图片描述

注:

  1. AVL 树它的任何一个节点的左右子树都是 AVL 树。
  2. AVL 树的查找、插入和删除在平均和最坏情况下都是O(logn)。

三、左旋与右旋

根据BTS树(二叉排序树)的性质,可以看出通过左旋和右旋操作后得到的树仍然是BTS树。
请添加图片描述

四、AVL树的调整

1、向AVL树中插入新数据

和BST一样,首先在叶节点插入数据。在 AVL 树中进行插入或删除节点后,可能导致 AVL 树失衡,需要对以插入路径上离插入节点最近的平衡因子绝对值大于1的节点为根的树进行调整。
这种失去平衡的可以概括为4种姿态:LL(左左),LR(左右),RR(右右),RL(右左)。
请添加图片描述

1)LL型不平衡(右单旋转)

在某个子树的左子树的左子树上插入新节点,导致子树根节点的平衡因子由1变为2。
请添加图片描述

2)RR型不平衡(左单旋转)

在某个子树的左子树的左子树上插入新节点,导致子树根节点的平衡因子由-1变为-2。
请添加图片描述

3)LR型不平衡(左右双旋转)

在某个子树的左子树的右子树上插入新节点,导致子树根节点的平衡因子由1变为2。
请添加图片描述
感觉这里最不好理解,举一个较复杂的样例。其根本思想就是Z节点先进行左旋后进行右旋。
原AVL树:
在这里插入图片描述
插入节点11:
在这里插入图片描述
将节点16左旋右旋:
在这里插入图片描述

4)RL型不平衡(右左双旋转)

在某个子树的右子树的左子树上插入新节点,导致子树根节点的平衡因子由1变为2。
请添加图片描述

五、代码实现

#include <iostream>
#include <algorithm>

class AVL_Node
{
public:
    friend class AVL_Tree;
    AVL_Node() : _key(0), _cnt(0), _height(0), _lchild(0), _rchild(0) {}
    AVL_Node(int key) : _key(key), _cnt(0), _height(0), _lchild(0), _rchild(0) {}
    int height(AVL_Node *node)
    {
        if (!node)
            return -1;
        return node->_height;
    }
    void update_height()
    {
        _height = 1 + std::max(height(_lchild), height(_rchild));
    }
    int getBalenceFactor()
    {
        return height(_lchild) - height(_rchild);
    }
    void preorder(AVL_Node *node, std::ostream &os) const
    {
        if (node)
        {
            os << node->_key << " ";
            if (node->_lchild)
                preorder(node->_lchild, os);
            if (node->_rchild)
                preorder(node->_rchild, os);
        }
    }

private:
    int _key;
    int _cnt;
    int _height;
    AVL_Node *_lchild;
    AVL_Node *_rchild;
};

class AVL_Tree
{
public:
    AVL_Tree() : _root(0) {}
    AVL_Tree(AVL_Node *root) : _root(root) {}
    void insert(const int &);
    AVL_Node *insert_key(AVL_Node *node, const int &);
    AVL_Node *rightRotate(AVL_Node *Y)
    {
        /* 树结构示意图:
                Y
               / \
              X   O
             / \
            O   O
        */
        AVL_Node *X = Y->_lchild;
        AVL_Node *tmp = X->_rchild;
        X->_rchild = Y;
        Y->_lchild = tmp;
        Y->update_height();
        X->update_height();
        return X;
    }
    AVL_Node *leftRotate(AVL_Node *Y)
    {
        /* 树结构示意图:
                Y
               / \
              O   X
                 / \
                O   O
        */
        AVL_Node *X = Y->_rchild;
        AVL_Node *tmp = X->_lchild;
        X->_lchild = Y;
        Y->_rchild = tmp;
        Y->update_height();
        X->update_height();
        return X;
    }

    void preOrder();

private:
    AVL_Node *_root;
};

void AVL_Tree::insert(const int &key)
{
    if (!_root)
    {
        _root = new AVL_Node(key);
    }
    else
    {
        _root = insert_key(_root, key);
    }
}

AVL_Node *AVL_Tree::insert_key(AVL_Node *node, const int &key)
{
    if (key == node->_key)
    {
        ++(node->_cnt);
        return node;
    }

    if (key < node->_key)
    {
        if (!node->_lchild)
            node->_lchild = new AVL_Node(key);
        else
            node->_lchild = insert_key(node->_lchild, key);
    }
    else if (key > node->_key)
    {
        if (!node->_rchild)
            node->_rchild = new AVL_Node(key);
        else
            node->_rchild = insert_key(node->_rchild, key);
    }

    // 更新路径上每个节点的高度。
    node->update_height();
    // 计算平衡因子。
    int p = node->getBalenceFactor();

    // LL型不平衡。
    if (p > 1 && key < node->_lchild->_key)
    {
        return rightRotate(node);
    }
    // RR型不平衡。
    if (p < -1 && key > node->_rchild->_key)
    {
        return leftRotate(node);
    }
    // LR型不平衡。
    if (p > 1 && key > node->_lchild->_key)
    {
        node->_lchild = leftRotate(node->_lchild);
        return rightRotate(node);
    }
    // RL型不平衡。
    if (p < -1 && key < node->_rchild->_key)
    {
        node->_rchild = rightRotate(node->_rchild);
        return leftRotate(node);
    }
    return node;
}

void AVL_Tree::preOrder()
{
    std::cout << "preOrder: ";
    if (_root)
        _root->preorder(_root, std::cout);
    std::cout << std::endl;
}

int main()
{
    AVL_Tree* avl = new AVL_Tree();
    avl->insert(10);
    avl->insert(20);
    avl->insert(30);
    avl->insert(40);
    avl->insert(50);
    avl->insert(25);
    
    avl->preOrder();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/108962.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

爆款短视频是怎样练成的:视频发布技巧,首次公开

剪辑好的优质短视频怎么发布才能成为爆款视频&#xff1f;短视频发布技巧公开 前面几篇我们讨论了短视频定位&#xff0c;怎么写文案脚本&#xff0c;怎么拍摄以及后期剪辑&#xff0c;至此我们一个优质的短视频已经制作完成&#xff0c;今天我们就聊一下下一个环节&#xff1…

Kafka Producer Retries Idempotence 原理

Kafka Producer Retries & Idempotence 原理 由于存在网络瞬时抖动&#xff1b;或者kafka集群短暂的不可用&#xff0c;会导致kafka producer发送消息出现异常&#xff0c;生产者无法将消息推送到topic&#xff0c;在这种情况下&#xff0c;消息丢失的可能性很高。因此kaf…

全排列思路

目录 省流版结论 推导过程 输出结果&#xff08;元素数量为4时&#xff09; 省流版结论 &#xff08;程序来源&#xff1a;排列组合之——全排列&#xff08;c语言&#xff09;_rewrite!的博客-CSDN博客_全排列&#xff09; 一晚上的时间&#xff0c;终于弄懂了。真羡慕计算…

基于标志点特征高精提取与匹配方法,进行双目、结构光、RGBD相机、单目相机多视拼接

1. 工作原理 人工张贴标志点变换位置拍照相邻照片的公共视野内有相同的标志点群匹配两张照片对应标志点对通过三对以上标志点对&#xff0c;实现两张照片间的坐标变换求解 2.标志点特征 圆形 分类&#xff1a; 编码&#xff08;粘贴于被测物体表面&#xff09;&#xff1a;…

SpringBoot文件上传(官方案例)

在线文档项目结构 1.源码克隆&#xff1a;git clone https://github.com/spring-guides/gs-uploading-files.git 2.包含两个项目initial和complete&#xff0c;initial可以根据文档练习完善&#xff0c;complete是完整项目 3.功能描述&#xff1a;构建接受文件上传的应用程序&a…

转行人必看:数字IC前端设计学习路线与方法(内附学习视频)

众所周知&#xff0c;数字前端设计对于工程师的能力要求比较高&#xff0c;不仅有学历上的要求&#xff0c;还要求掌握很多的知识技能。不少跨专业想要转行的小伙伴对数字前端设计这个岗位不是很了解&#xff0c;下面IC修真院就带大家全面了解一下数字IC前端设计。 数字前端到…

酒水销售网站

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 网站前台&#xff1a;网站介绍、帮助信息、酒水资讯、酒水类型、酒水信息、购物分享 管理员&#xff1a; 1、管理网站介…

matlab源码说明

目录 1.MATLAB概述 2.MATLAB程序使用几个常规注意实现 2.1.运行过程可能出现Out of Memory的问题解决办法 2.2.保存大于2G的数据 2.3.程序运行方法 1.MATLAB概述 Matlab经过不断的发展和完善,如今已成为覆盖多个学科,是具有超强数值计算能力和仿真分析能力的软件。…

YOLOv5s.yaml文件解读

目录一、YOLOv5s.yaml内容二、详解2.1参数配置2.2 anchors2.3 backbone2.4 head三、如何调整模型一、YOLOv5s.yaml内容 YOLOv5配置了4种不同大小的网络模型&#xff0c;分别是YOLOv5s、YOLOv5m、YOLOv5l、YOLOv5x&#xff0c;其中YOLOv5s是网络深度和宽度最小但检测速度最快的…

资深车主才会告诉你的那些事,看完立省三万二

自动驾驶已经火了几年了。在某度一搜“自动驾驶”&#xff0c;全都是某某公司推出了自动驾驶&#xff0c;某某公司打算推出自动驾驶。当然&#xff0c;这两年因为各种事故的原因&#xff0c;“自动驾驶”被各种名词取代——辅助驾驶、高级辅助驾驶、领航辅助驾驶......这就有点…

以英雄之名为S9总决赛助攻! 虎牙直播and华为云CDN,team work才会赢

以英雄之名为S9总决赛助攻! 虎牙直播and华为云CDN&#xff0c;team work才会赢 你的朋友圈肯定经历过这样一波刷屏 啊啊!!!赢了祝贺!#FPX团灭G2#FPX夺冠啦!超帅超酷超级棒!我们是冠军!!!恭喜FPX!凤凰涅槃!FPX咋这么稳!实在太稳了!小凤凰一飞冲天 团灭!就两个字 就是感觉很厉害…

SimpleAdapter的简单使用

SimpleAdapter适配器的简单使用 1.SimpleAdapter 1.简介 显示复杂的列表项&#xff0c;如图片2.SimpleAdapter的构造函数 上下文数据&#xff08;保存map的list集合&#xff09;子布局key子布局中的id注意&#xff1a;key和id是一一对应的关系 3.SimpleAdapter案例-用户简介 3.…

Java面试常用,初级到高级(持续更新中....)

文章目录一、java基础1、基本类型2、String相关3、数组和list相互转换4、java中的锁5、常用的算法二、项目层面1、并发与并行的区别三、常见错误解决1、Java ConcurrentModificationException异常原因和解决方法2、 main中不能使用this四、底层的相关技术1、spring如何处理循环…

54.Python的def语句自定义函数

54.def语句自定义函数 文章目录54.def语句自定义函数1.课题导入—数学中的函数1.1 计算圆的面积1.2 计算不规则图形的面积2. 什么是函数3. 函数分类3.1 内置函数3.2 自定义函数4. 计算多个矩形的面积5. 自定义函数计算矩形的面积6.自定义函数的写法1.先写函数要实现的功能2.添加…

Android设计模式详解之策略模式

前言 定义&#xff1a;定义了一系列的算法&#xff0c;并将每一个算法封装起来&#xff0c;而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。 使用场景&#xff1a; 针对同一类型问题的多种处理方式&#xff0c;仅仅是具体行为有差别时&#xff1b;…

电磁波的达朗贝尔方程 工程电磁场P25

我们有下述两个方程 记住一定是线性介质 称为电磁波的达朗贝尔方程&#xff0c;是两个非齐次方程 我们下面介绍达朗贝尔方程的解 我们就用最简单的场源举例 这个方程可以写成 动态位是r和时间的函数 我们可以在球坐标系下展开成 我们再做一下变换 现在如果在这个微分方程里面…

智牛股_第7章_OAuth2+Spring Security OAuth2+GateWay+Druid+Spring Data JPA

交易平台 - Day 7 文章目录交易平台 - Day 7学习目标第1章 OAUTH2统一认证1. 目标2. 分析3. 讲解3.1 传统登陆认证3.2 单点登陆认证3.3 OAuth2简介3.4 OAuth2角色3.5 OAuth2 协议流程3.6 授权类型3.7 授权码模式流程3.8 简化模式3.9 密码模式3.10 客户端模式3.11 Spring Securi…

分治法算法

目录 一 算法简介 ①算法定义 ②算法特征 ③算法编程 二 算法应用 汉诺塔 问题描述 问题解析 问答解答 快速幂 问题描述 问题解析 问题解答 三 分治思想设计排序算法 步骤解析 归并排序 归并排序的主要操作 归并排序与交换排序 归并算法的应用&#xff1a;逆…

前端基础(十六)_BFC、box-shadow(盒子阴影)、text-shadow(文字阴影)

什么是BFC&#xff1f; 1、块级格式化上下文&#xff0c;web页面可视化css视觉渲染的一部分&#xff0c;是页面中一个独立的渲染区域&#xff1b; 2、用于决定块盒子的布局和浮动相互影响范围的一个区域&#xff0c;可以改变父级元素的渲染规则&#xff1b; 3、无论里面怎么折…

轻松入门基因表达式编程 (GEP)

0 引言 基因表达式编程 GEP (Gene Expression Programming) 是一种基于生物基因结构和功能发明的一种新型自适应演化算法。GEP 是从遗传算法&#xff08;geneticalgorithms ,简称 GAs&#xff09;和遗传程序设计&#xff08;genetic pro2gramming ,简称 GP&#xff09;中发展而…